i have a input file which is used to upload images. However, I have to validate its size before upload. As below is the code I've tried on.
I have a parent function, that calls the method,ValidateImageSize( ):
$('input:file').change(function (e) {
if (ValidateImageSize(this))
// do something
else
alert("wrong size");
});
and the method shown as below:
var ValidateImageSize = function (input) {
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.onload = function (e) {
return this.height == 40 && this.width == 40 ? true : false;
}
img.src = e.target.result;
}
reader.readAsDataURL(input.files[0]);
};
The method ValidateImageSize() always returns 'undefined' to its parents, cause it have yet executes the onload functions.
The output I need is either true/ false. Perhaps the structure of my codes itself is incorrect.
How can I solve this?
Use callback, something like as below:
var validation_callback = function(isValid) {
if (isValid) {
// do something
} else {
alert("wrong size");
}
};
$('input:file').change(function(e) {
ValidateImageSize(this, validation_callback);
});
var ValidateImageSize = function (input, callback) {
var reader = new FileReader();
reader.onload = function (e) {
var img = new Image();
img.onload = function (e) {
var isValid = this.height == 40 && this.width == 40 ? true : false;
callback(isValid);
};
img.src = e.target.result;
};
reader.readAsDataURL(input.files[0]);
};
Related
Actually I have a form where with image input. I want to validate image for three condition like
extension should be png, jpg
size should be less than 2048kb
less than 200px x 200px is consider as dimension
I wrote an function and solve 1 and 2 issue. To solve issue 3 , I use image reader inside onload listener and when I clicked it can not prevent submit also if I remove 3, then it works fine ! Is there any solution in JS that solve above issue?
Here is a slide of my code in below.
function isImageValid(idName) {
var fileUpload = document.getElementById(idName);
var fileName = document.getElementById(idName).value;
if (typeof (fileUpload.files) != "undefined") {
for (var i=0; i<fileUpload.files.length;i++)
{
// console.log(fileUpload.files[i]);
var valid_dimension = 0;
var reader = new FileReader();
//Read the contents of Image File.
reader.readAsDataURL(fileUpload.files[0]);
reader.onload = function (e) {
//Initiate the JavaScript Image object.
var image = new Image();
//Set the Base64 string return from FileReader as source.
image.src = e.target.result;
//Validate the File Height and Width.
image.onload = function () {
var height = this.height;
var width = this.width;
if (height>200||width>200) {
valid_dimension =1;
// alert("Height and Width must not exceed 200px.");
return false;
}
// alert("Uploaded image has valid Height and Width.");
return true;
};
};
var size = parseFloat(fileUpload.files[0].size / 1024).toFixed(2);
var extension = fileName.split('.').pop();
if( valid_dimension ==1||size>2048||(extension!='jpg'&&extension!='JPG'&&extension!='JPEG'&&extension!='jpeg'&&extension!='png'&&extension!='PNG'))
return false;
else
return true;
}
} else {
return false;
}
}
And,
const form = document.getElementById('employee_form');
form.addEventListener('submit', (e)=>{
var is_avatar_img_valid = isImageValid('avatar');
if(is_avatar_img_valid==false){
e.preventDefault();
document.getElementById("avatar").style.borderColor = "red";
document.getElementById('avatar_validator_message').innerHTML = 'Invalid image';
}
else{
document.getElementById("avatar").style.borderColor = "black";
document.getElementById('avatar_validator_message').innerHTML = '';
}
}
The problem is reader.onload and image.onload functions are async in nature. So your form submits before these onload methods execute.
To solve this you need to follow the below steps
Prevent default in submit event handler
Pass callbacks for valid and invalid image to the isImageValid function
Manually submit the form if image is valid
Below is the code. Please mark the answer as accepted, if it helps
function isImageValid(idName, onValidCallback, onInvalidCallback) {
var fileUpload = document.getElementById(idName);
var fileName = document.getElementById(idName).value;
if (typeof (fileUpload.files) != "undefined") {
for (var i = 0; i < fileUpload.files.length; i++) {
// console.log(fileUpload.files[i]);
//--------------------
const allowedExtension = ['jpg', 'JPG', 'JPEG', 'jpeg', 'png', 'PNG'];
const maxAllowedSize = 2048;
const maxAllowedHeight = 200;
const maxAllowedWidth = 200;
const size = parseFloat(fileUpload.files[i].size / 1024).toFixed(2);
const extension = fileName.split('.').pop();
console.log({ size, extension });
//Check for valid extension and size limit
if (allowedExtension.some(ext => ext === extension) && size <= maxAllowedSize) {
//Extension and size are valid
// Now check for valid dimensions
const reader = new FileReader();
reader.readAsDataURL(fileUpload.files[i]);
reader.onload = function (e) {
//Initiate the JavaScript Image object.
var image = new Image();
//Set the Base64 string return from FileReader as source.
image.src = e.target.result;
//Validate the File Height and Width.
image.onload = function () {
const height = this.height;
const width = this.width;
console.log({ height, width });
if (height > maxAllowedHeight || width > maxAllowedWidth) {
// alert("Height and Width must not exceed 200px.");
//File does not meet the dimensions guidline
if (onInvalidCallback)
onInvalidCallback();
return false;
}
// alert("Uploaded image has valid Height and Width.");
//Everything is fine, form canbe submitted now
if (onValidCallback)
onValidCallback();
};
};
}
break;
}
}
else {
// There are no files selected
if (onInvalidCallback)
onInvalidCallback();
}
}
const form = document.getElementById('employee_form');
form.addEventListener('submit', (e) => {
e.preventDefault();
isImageValid('avatar', () => {
alert('going to submit');
document.getElementById("avatar").style.borderColor = "black";
document.getElementById('avatar_validator_message').innerHTML = '';
//Manually submit the form
form.submit();
},
() => {
alert('stop submit');
document.getElementById("avatar").style.borderColor = "red";
document.getElementById('avatar_validator_message').innerHTML = 'Invalid image';
}
);
return false;
});
Inside the form eventlister you need to use
e.preventDefault()
The full code looks like this
const form = document.getElementById('employee_form');
form.addEventListener('submit', (e) => {
e.preventDefault()
var is_avatar_img_valid = isImageValid('avatar');
if (is_avatar_img_valid == false) {
e.preventDefault();
document.getElementById("avatar").style.borderColor = "red";
document.getElementById('avatar_validator_message').innerHTML = 'Invalid image';
}
else {
document.getElementById("avatar").style.borderColor = "black";
document.getElementById('avatar_validator_message').innerHTML = '';
}
});
I have vue component like this :
<template>
<section>
...
</section>
</template>
<script>
export default {
...
data() {
return {
allowableTypes: ['jpg', 'jpeg', 'png'],
maximumSize: 4000000
}
},
methods: {
...
onFileChange(e) {
if (this.validate(e.target.files[0])) {
let files = e.target.files,
reader = new FileReader()
// if any values
if (files.length){
reader.onload = (e) => {
this.image = e.target.result
}
reader.readAsDataURL(files[0])
}
}
},
validate(image) {
// validation file type
if (!this.allowableTypes.includes(image.name.split(".").pop().toLowerCase())) {
return false
}
// validation file size
if (image.size > this.maximumSize) {
return false
}
// validation image resolution
let img = new Image()
img.src = window.URL.createObjectURL(image)
let self = this
img.onload = function() {
let width = img.naturalWidth,
height = img.naturalHeight
window.URL.revokeObjectURL(img.src)
if(width != 850 && height != 350) {
return false
}
}
return true
}
}
}
</script>
If user upload image, it will call onFileChange method. Before displaying the image, it will call method validate to validate the image.
I try to validate file size and file type and it works. The problem here is validating the resolution.
From my code, it seems my code is true
But when I try like this:
I upload image with width = 100, height = 100, from the code, should it return `false``.
But when I run my code, it returns true.
Seems return is not working in the img.onload
How can I solve this problem?
A nice way to handle asynchronous validation is by using Promises :
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
If you are targeting Internet Explorer, make sure to use a polyfill such as this one :
https://github.com/stefanpenner/es6-promise
Your code would then look like this :
onFileChange(e) {
let self = this
this.validate(e.target.files[0])
.then(function() {
let files = e.target.files,
reader = new FileReader()
// if any values
if (files.length) {
reader.onload = (e) => {
self.image = e.target.result
}
reader.readAsDataURL(files[0])
}
})
.catch(function() {
// do something in the case where the image is not valid
})
},
validate(image) {
let self = this
return new Promise(function(resolve, reject) {
// validation file type
if (!self.allowableTypes.includes(image.name.split(".").pop().toLowerCase())) {
reject()
}
// validation file size
if (image.size > self.maximumSize) {
reject()
}
// validation image resolution
let img = new Image()
img.src = window.URL.createObjectURL(image)
img.onload = function() {
let width = img.naturalWidth,
height = img.naturalHeight
window.URL.revokeObjectURL(img.src)
if (width != 850 && height != 350) {
reject()
} else {
resolve()
}
}
})
}
If you do not want or cannot use Promises you could use a Callback to achieve the same behaviour :
onFileChange(e) {
let self = this
let validCallback = function() {
let files = e.target.files,
reader = new FileReader()
// if any values
if (files.length) {
reader.onload = (e) => {
self.image = e.target.result
}
reader.readAsDataURL(files[0])
}
}
let unvalidCallback = function() {
// do something in the case where the image is not valid
}
this.validate(e.target.files[0], validCallback, unvalidCallback)
},
validate(image, validCallback, unvalidCallback) {
// validation file type
if (!this.allowableTypes.includes(image.name.split(".").pop().toLowerCase())) {
unvalidCallback()
return
}
// validation file size
if (image.size > this.maximumSize) {
unvalidCallback()
return
}
// validation image resolution
let img = new Image()
img.src = window.URL.createObjectURL(image)
let self = this
img.onload = function() {
let width = img.naturalWidth,
height = img.naturalHeight
window.URL.revokeObjectURL(img.src)
if (width != 850 && height != 350) {
unvalidCallback()
return
} else {
validCallback()
}
}
}
It's onloadend not onload.
Change your code to this:
let self = this;
var reader = new FileReader();
reader.onloadend = () => {
// you logic here (use self, not this)
}
I am attempting to access the dimensions from my uploaded image before I post it to the post upload endpoint. If you look, there is a variable issueArrived that is supposed to determine if the image is too small and if so negate the upload process. Unfortunately, the image doesn't access the dimensions check until after the upload to the server commences. Any help would be appreciated.
// internal function that creates an input element
var file = newElement("input", 0, "");
// sets the input element as type file
file.type = "file";
file.multiple = "multiple";
file.name = "photoupload[]";
file.accept = "image/*";
var issueArrived = false;
file.onchange = function(e) {
// internal function that creates an image element
var img = newElement("img", 0, "");
img.onload = function(){
var cw = img.width;
var ch = img.height;
if(cw < 500 || ch < 500) {
alert("Photo must be atleast 500x500");
issueArrived = true;
return false;
}
}
img.src = URL.createObjectURL(this);
if (issueArrived) return;
var formdata = new FormData();
formdata.append("photoupload[]", this);
var x = new XMLHttpRequest();
x.open("POST", "/media/uploadphotos", true);
x.send(formdata);
}
file.click();
onchange method is async. You can't expect to set the value from issueArrived inside it an access it outside.
You can rewrite this way:
file.onchange=function(e){
// internal function that creates an image element
var img=newElement("img",0,"");
img.onload=function(){
var cw=img.width;
var ch=img.height;
if(cw<500||ch<500){
alert("Photo must be atleast 500x500");
} else {
var formdata=new FormData();
formdata.append("photoupload[]",this);
var x=new XMLHttpRequest();
x.open("POST","/media/uploadphotos",true);
x.send(formdata);
}
}
img.src=URL.createObjectURL(this);
}
file.click();
or, better, you can create a function that gets the file and returns a promise.
function checkSize(fileToCheck) {
return new Promise( function(resolve, reject) {
var img = newElement("img",0,"");
img.onload=function(){
var cw=img.width;
var ch=img.height;
cw<500||ch<500 ? reject() : resolve()
}
img.src=URL.createObjectURL(fileToCheck);
})
}
...
file.onchange=function(e){
checkSize(this)
.then( function() { /* success handler */ } )
.catch( function() { /* error handler */ } )
}
The following are the variables i have declared in javascript script tag.
The Variables are declared globally and initialized in the inner function which is inside another function (outer).
The problem is; when i try to check the variables using console.log i get "undefined". I don't know why.
I have read the variable hoisting, scope etc of Javascript variables. But i am unable to solve it. Please help.
var width;
var height;
var ratio;
var nwidth;
var nheight;
var wbool;
var imgsrc;
var canvas;
$("#myimage").change(function(){
readURL(this);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function(){
if(this.width > this.height){
ratio = this.width/this.height;
nheight = parseInt(Math.ceil(1024/ratio));
wbool = true;
}
else{
ratio = this.height/this.width;
window.nwidth = parseInt(Math.ceil(768*ratio));
wbool = false;
}
}
console.log(window.nwidth + "" + wbool);
You're console.log()'ing nwidth before it has been assigned.
image.onload is an asynchronous operation (think like a callback). the console.log() is firing before the variable data has been assigned.
Try moving the log into the onload function.
var width;
var height;
var ratio;
var nwidth;
var nheight;
var wbool;
var imgsrc;
var canvas;
$("#myimage").change(function(){
readURL(this);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function(){
if(this.width > this.height){
ratio = this.width/this.height;
nheight = parseInt(Math.ceil(1024/ratio));
wbool = true;
}
else{
ratio = this.height/this.width;
window.nwidth = parseInt(Math.ceil(768*ratio));
wbool = false;
// it will be defined here, because you are doing
// it inside the async operation.
console.log(window.nwidth);
}
}
// this will be undefined, because the var has not been set
// by the time this line executes.
console.log(window.nwidth + "" + wbool);
why does this happen?
async operations, like ajax calls ( or file loading in your code), take time to complete. Since javascript runs on a single thread, if the app were to stop and wait for the operation to complete, then the browser would lock up. Not good!
So, async operations are skipped over, and the callback portion is executed once the operation completes. This means your code is executing like this:
start loading the file
console.log() // undefined
loading complete, and the variable has been defined.
To trigger a custom event, you could do something like this:
$("#myimage").change(function(){
readURL(this);
});
// SET UP LISTENER FOR YOUR CUSTOM EVENT
$(document).on('fileWasLoaded', function () {
console.log(window.nwidth + "" + wbool);
});
function readURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function(){
if(this.width > this.height){
ratio = this.width/this.height;
nheight = parseInt(Math.ceil(1024/ratio));
wbool = true;
}
else{
ratio = this.height/this.width;
window.nwidth = parseInt(Math.ceil(768*ratio));
wbool = false;
// TRIGGER YOUR CUSTOM EVENT WHEN THE IMAGE IS LOADED
$(document).trigger('fileWasLoaded');
}
}
The follow code isn't working for me in FF/IE. Chrome works. The problem seems to be that the function 'handleImage' ins't be called. What goes wrong here?
Thanks
function handleImage(event) {
console.log('Handle the image');
var reader = new FileReader();
reader.onload = function (event) {
$('#uploader img').attr('src', event.target.result);
}
if (event.target.files[0] != undefined) {
reader.readAsDataURL(event.target.files[0]);
imageLoaderError = false
} else {
imageLoaderError = true
}
}
var imageLoader = document.getElementById('image');
var imageLoaderError = false;
imageLoader.addEventListener('change', handleImage, false);