I have an image uploader where I have some PHP checks that happen on the server side. What I want to do is also provide frontend validations for the user, but I am having an issue getting the values to do the maths for the image resolution in megapixels. On the server side I can just use the file's width and height properties and then multiply them.
The problem I have is on the frontend when using a new Image()
object, when I get the width and height properties it gives me the values of the image file in the container, not the width and height values of the source file / original image.
I thought I could just use the .src
property and get the size of that instead, but this doesn't work either.
In the code below as you can see the Image()
object is assigned to a thumbnailElement
variable.
If you click the Select Files
button and add an image it will show the thumbnails and log the original file size to the console, but not the original width ?
Note: the updateThumbnail() function is invoked in the HTML
Codepen Link: https://codepen.io/thechewy/pen/yLjvqWe
var zone = document.getElementById("zone"),
selectedImagesContainer = document.getElementById("show-selected-images"),
fileUploader = document.getElementById("standard-upload-files");
zone.addEventListener("click", (e) => {
// assigns the zone element to the hidden input element so when you click 'select files' it brings up a file picker window
fileUploader.click();
});
fileUploader.addEventListener("change", (e) => {
// this function is further down but declared here and shows a thumbnail of the image
[...fileUploader.files].forEach(updateThumbnail);
});
function updateThumbnail(file) {
let uploadImageWrapper = document.createElement("figure"), // image wrapper <figure> element
thumbnailElement = new Image(); // image
// image thumbnail
thumbnailElement.classList.add("thumbnail");
thumbnailElement.src = URL.createObjectURL(file);
// appending elements
selectedImagesContainer.append(uploadImageWrapper); // append <figure> element
uploadImageWrapper.append(thumbnailElement); // append image thumbnail
console.log("file size is: ", file.size);
console.log("file width is: ", thumbnailElement.width);
} // end of 'updateThumbnail' function
body {
margin: 0;
display: flex;
justify-content: center;
font-family: arial;
}
form {
width: 50%;
max-width: 600px;
}
.select-files {
padding: 1rem;
background: red;
cursor: pointer;
color: #fff;
font-weight: bold;
}
#show-selected-images {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 2rem;
}
figure {
width: 100%;
margin: 0;
}
img {
display: block;
width: 100%;
height: auto;
}
<form id="upload-images-form" enctype="multipart/form-data" method="post">
<h1>Upload Your Images</h1>
<div id="zone">
<p >SELECT FILES</p>
</div>
<div >
<div >
<input id="standard-upload-files" style="display:none;" type="file" name="standard-upload-files[]" multiple>
</div>
<button id="submit-images" oninput="updateThumbnail(this.files)">SUBMIT IMAGES</button>
</div>
<div id="show-selected-images"></div>
</form>
CodePudding user response:
You need the naturalWidth property to get actual image width in pixels, also to get that value reliably you should use the HTMLImageElement.decode() method that returns a promise which is resolved once the full-resolution image is fully decoded, like this:
var zone = document.getElementById("zone"),
selectedImagesContainer = document.getElementById("show-selected-images"),
fileUploader = document.getElementById("standard-upload-files");
zone.addEventListener("click", (e) => {
// assigns the zone element to the hidden input element so when you click 'select files' it brings up a file picker window
fileUploader.click();
});
fileUploader.addEventListener("change", (e) => {
// this function is further down but declared here and shows a thumbnail of the image
[...fileUploader.files].forEach(updateThumbnail);
});
function updateThumbnail(file) {
let uploadImageWrapper = document.createElement("figure"), // image wrapper <figure> element
thumbnailElement = new Image(); // image
// image thumbnail
thumbnailElement.classList.add("thumbnail");
thumbnailElement.src = URL.createObjectURL(file);
// appending elements
selectedImagesContainer.append(uploadImageWrapper); // append <figure> element
uploadImageWrapper.append(thumbnailElement); // append image thumbnail
console.log("file size is: ", file.size);
thumbnailElement.decode().then(() => {
console.log("file width is: ", thumbnailElement.naturalWidth);
}).catch((encodingError) => {
// Do something with the error.
});
} // end of 'updateThumbnail' function
body {
margin: 0;
display: flex;
justify-content: center;
font-family: arial;
}
form {
width: 50%;
max-width: 600px;
}
.select-files {
padding: 1rem;
background: red;
cursor: pointer;
color: #fff;
font-weight: bold;
}
#show-selected-images {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-top: 2rem;
}
figure {
width: 100%;
margin: 0;
}
img {
display: block;
width: 100%;
height: auto;
}
<form id="upload-images-form" enctype="multipart/form-data" method="post">
<h1>Upload Your Images</h1>
<div id="zone">
<p >SELECT FILES</p>
</div>
<div >
<div >
<input id="standard-upload-files" style="display:none;" type="file" name="standard-upload-files[]" multiple>
</div>
<button id="submit-images" oninput="updateThumbnail(this.files)">SUBMIT IMAGES</button>
</div>
<div id="show-selected-images"></div>
</form>
CodePudding user response:
Yes, .width
and .height
will return the computed width and height of the element when it's visible in the doc.
Since your <img>
element doesn't have an srcset
attribute, you can use both its naturalWidth
and naturalHeight
to get the actual intrinsic size of the loaded image. If you had this srcset
attribute though, these values could be divided by the size
multiplier of the loaded source.
If you weren't displaying the image in a thumbnail, an efficient way to get the dimensions of the image would have been to call createImageBitmap()
either on the File
object you have directly, and to check the .width
and .height
properties of the resolved ImageBitmap
object.
But since you do show the image, in your case that'd be overkill. Using .naturalWidth
and .naturalHeight
(after the image has loaded) is just enough.