checking image dimensions before ajax post event in javascript - javascript

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 */ } )
}

Related

How to validate an single/multiple image in Javascript and show error message below the input with prevent default submit?

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 = '';
}
});

How to call a function that gives a url, which is then used to store and display an image using FileReader

I am new to coding in javascript, I have been tasked as an excersise using some pre-existing functions to create an image item using a given url. I need to read the image, store it in local storage and then display it on the screen. The only functionI need to add too is process file. I am stuck and cannot get it to display the image. I get an error saying that the path does not exist. Below is my code, any guidance to what I am doing wrong? I feel like I am very close but doing something funamendatly wrong.
function addImageEntry(key, url) {
var imgElement = new Image();
imgElement.alt = "Photo entry";
imgElement.src = url;
addSection(key, imgElement);
}
function makeItem(type, data) {
var itemObject = { type: type, data: data };
return JSON.stringify(itemObject);
}
function processFile(event) {
function addImage(url) {
var key = "diary" + Date.now();
addImageEntry(key, url);
var imgElement = new Image();
imgElement.src = url;
var item = makeItem("image", imgElement.src);
localStorage.setItem(key, item);
}
var inputElement = event.target;
var fileObject = inputElement.files[0];
var reader = new FileReader();
reader.addEventListener("load",addImage);
url = reader.readAsDataURL(fileObject);
event.target.value = "";
}
The url = reader.readAsDataURL(fileObject) won't store the image data.
It stores in reader.result.
Use imgElement.src = reader.result.
document.querySelector('input[type="file"]').addEventListener('change', processFile)
function processFile(event) {
var fileObject = event.target.files[0]
var reader = new FileReader()
reader.addEventListener('load', addImage)
reader.readAsDataURL(fileObject)
function addImage() {
var imgElement = new Image()
imgElement.src = reader.result
document.body.append(imgElement)
}
}
Amend:
function addImage(event) {
// use reader.result is ok too
localStorage.setItem('myimage', event.target.result)
}
function addImageEntry() {
var imgElement = new Image()
imgElement.src = localStorage.getItem('myimage')
imgElement.style.display = 'block'
document.body.append(imgElement)
}

jQuery: Multi image dimension validation before upload

I'm trying to valdiate a multiple image upload using jQuery. The validation proccess get the images dimensions and check if it is greater than 338x450. But I know very little about javascript. Can someone help me? Here's the code I'm trying:
HTML:
<input class="form-control" id="ads-photos" name="ads-photos[]" type="file" accept="image/jpeg, image/jpg" multiple />
javascript
var isFilesOk;
$('#ads-photos').change(function (evt) {
validateDimensions();
});
function validateDimensions() {
var fi = document.getElementById('ads-photos');
if (fi.files.length > 0) {
for (var i = 0; i <= fi.files.length - 1; i++) {
var fileName, fileExtension, fileSize, fileType, dateModified;
fileName = fi.files.item(i).name;
fileExtension = fileName.replace(/^.*\./, '');
if (fileExtension == 'jpg' || fileExtension == 'jpeg') {
if (readImageFile(fi.files.item(i))) {
alert(isFilesOk);
} else {
alert(isFilesOk);
}
}
}
}
// GET THE IMAGE WIDTH AND HEIGHT USING fileReader() API.
function readImageFile(file) {
var reader = new FileReader(); // CREATE AN NEW INSTANCE.
reader.onload = function (e) {
var img = new Image();
img.src = e.target.result;
img.onload = function () {
var w = this.width;
var h = this.height;
if (this.width >= 338 && this.height >= 450) {
isFilesOk = true;
} else {
isFilesOk = false;
}
}
};
reader.readAsDataURL(file);
return isFilesOk;
}
}
A example link
The above code needs analyze all the images sended by the user to know if they all have minimum required dimensions. But I'm not getting it to work.
The issue here is that the readImageFile method is asynchronous. The work it's doing happens later, after it's executed when the onload event is triggered on the file.
The best solution will depend a little on the environment you're targeting (eg: whether you need to support old IE, etc), but if you're able to use modern JS with Promises, this will be easier.
Basically, for each file that's selected, you want to get a Promise indicating whether the file is okay or not. A good way to do this is map the array to an array of Promises and use Promise.all to get a callback when they're all done.
Here's the method to get that for a single file:
function fileIsValid(file) {
return new Promise((resolve) => {
var reader = new FileReader(); // CREATE AN NEW INSTANCE.
reader.onload = function (e) {
var img = new Image();
img.src = e.target.result;
img.onload = function () {
var w = this.width;
var h = this.height;
const isValid = w >= 338 && h >= 450;
resolve(isValid);
};
};
reader.readAsDataURL(file);
});
}
And then using this method:
const fi = document.getElementById('ads-photos');
const promises = [];
for (let i = 0; i <= fi.files.length - 1; i++) {
const file = fi.files.item(i);
if (/\.jpe?g$/.test(file.name)) {
promises.push(fileIsValid(file));
}
}
Promise.all(promises).then((results) => {
// results is an array of booleans
const allValid = results.every(Boolean);
if (allValid) {
alert('Yeah!');
} else {
alert('Nope');
}
});
There's a few things to note here:
you could make it shortcut once a single error is found by making the promise reject if it's invalid, and Promise.all then won't wait for any other pending promises. This is a bit of a style question about whether failed validation should result in a rejected promise. Up to you on that. In this case, it's reading from a local file system and I presume they'll be selecting a relatively small number of files, so short-circuiting probably won't make any noticeable difference.
it doesn't handle actual errors (eg: what if a file is unable to be loaded?).

How do I read value of variable defined in FileReader.onloadend inside XMLHttpRequest.onreadystatechange callback functin?

In the code below, I need read the value of variable ext (defined inside the function reader.onloadend) in the function xhr.onreadystatechange. Right now is returning undefined. Any ideas how to fix that?
function file_upload(file_input) {
var name = file_input.dataset.target;
var url = file_input.dataset.url;
var path = file_input.dataset.path;
var ext;
for(var i = 0; i<file_input.files.length; i++) {
var file = file_input.files[i];
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var id = xhr.responseText;
var input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", name);
input.setAttribute("value", id);
document.getElementById("btn_uload_"+name+"_"+ext).append(input);
document.getElementById("empty_file").style.display = 'none';
document.getElementById("single_file_"+ext).style.display = 'block';
}
};
var reader = new FileReader();
reader.onloadend = function() {
var bytes = reader.result;
var ext = file.name.split(".").pop();
var formData = new FormData();
formData.append('bytes', bytes);
formData.append('type', ext);
xhr.send(formData);
}
reader.readAsDataURL(file);
}
}
Remove the var ext =... from the reader callback that is only making a local variable for that function (it isnt setting the external one. Even if you remove the var keyword so that it starts setting the external ext variable you will end up hitting an issue discussed at this q&a
Move your assignment to the top of the loop(not the top of the function). And use let instead of var that way it is block scoped and wont run into the previously mentioned issue
for(var i = 0; i<file_input.files.length; i++) {
var file = file_input.files[i];
let ext = file.name.split('.').pop();

$ outside of jQuery

This app https://developer.mozilla.org/en-US/demos/detail/meet-me-there/launch accepts uploaded photos, attaches a qr code to them, and then allows you to share them. I've attached below the JavaScript for everything except the QR functionality. The app doesn't use jQuery, but at the beginning it assigns a function to the $.
window.onload = function(){
var $ = function(id){
console.log(id);
return document.getElementById(id);
},
When I run the app with the console.log in the above position, it shows that quite a few 'ids' are getting passed through that console.log(id). As the file loads, it logs 'surface', 'cam' and 'upload' and, as you engage the app, it logs 'result', 'sharer', 'uploadedURL' and many others. Problem is that I don't see how everything keeps getting passed through that function for the console.log to log the different 'id's at that point in the code. Therefore, I wonder what the significance of '$' is in this context (no jQuery). Specifically, by creating that '$' function, is it called anytime that any of the other events with $ are run, such as $('upload').onclick = function(){...
Is it working similar to how adding a prototype is working in jquery by using $.prototype.function() in jquery. If so, where does it get this functionality from if there's no jQuery.
window.onload = function(){
var $ = function(id){
console.log(id);
return document.getElementById(id);
},
canvas = $('surface'),
ctx = canvas.getContext('2d'),
watcher, loc='No location provided', located;
$('cam').onchange = function(event){
console.log(event);
console.trace();
var files = event.target.files,
file;
if (files && files.length > 0) {
file = files[0];
try {
var URL = window.URL || window.webkitURL || window.mozURL;
var imgURL = URL.createObjectURL(file);
var img = new Image();
img.id = "tester";
//Load it onto the canvas
img.onload = function() {
console.trace();
canvas.width = this.width;
canvas.height = this.height;
$('info').innerHTML = ("Width: " + this.width + "px, Height: " + this.height + "px");
$('result').width = 400;
$('result').height = (400 / (this.width/this.height)) >> 0;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var codeSize = (canvas.height/4) >> 0;
var imgn = new Image();
imgn.onload = function(){
ctx.drawImage(imgn, (canvas.width-5- codeSize), (canvas.height-5-codeSize), codeSize, codeSize);
$('result').src = canvas.toDataURL();
}
imgn.src = (QR.encode(loc, codeSize, 5));
}
img.src = imgURL;
} catch (e) {
console.log("error: " + e);
}
}
},
// borrowed this functionality from cgack's demo
// https://developer.mozilla.org/en-US/demos/detail/snap-and-share
$('upload').onclick = function(){
$('infomsg').style.display = 'block';
var url = "http://api.imgur.com/2/upload.json",
params = new FormData(),
http = new XMLHttpRequest();
params.append('key','29a8b1db1d8fda0df87006def2307242');
params.append('image',canvas.toDataURL().split(',')[1]);
http.open("POST", url);
http.onload = function() {
var url = JSON.parse(http.responseText).upload.links.imgur_page;
$('uploadedUrl').href = url;
$('uploadedUrl').innerHTML = url;
$('shareFb').href = ("http://www.facebook.com/sharer.php?u="+url);
$('shareTwitter').href = ("http://twitter.com/intent/tweet?url="+url);
$('sharer').style.display = 'block';
$('infomsg').style.display = 'none';
};
http.send(params);
console.log(params);
};
watcher = navigator.geolocation.watchPosition(function(position){
console.trace();
console.log("navigator");
loc = "geo:" + position.coords.latitude + "," +position.coords.longitude;
located = true;
}, function(error){
if(error.code == error.PERMISSION_DENIED || error.code == error.POSITION_UNAVAILABLE)
alert('damn, we were not able to locate you. sorry.');
}
);
};
$ is just a variable name, like any other. It has no special meaning.
"Problem is that I don't see how everything keeps getting passed through that function for the console.log to log the 'id' at that point in the code."
Any time you see $, it's a reference to the function. So a () after it invokes it with the given argument. It's just like any other function, just with a funny name referencing it.
"Therefore, I wonder what the significance of '$' is in this context (no jQuery). Specifically, by creating that '$' function, is it called anytime that any of the other events with $ are run"
Again, no real significance. It's just a function name. If you renamed all the instances of $ to byId, it would behave the same.
window.onload = function(){
// var $ = function(id){
var byId = function(id){
console.log(id);
return document.getElementById(id);
},
canvas = foo('surface'),
ctx = canvas.getContext('2d'),
watcher, loc='No location provided', located;
byId('cam').onchange = function(event){
/* a bunch of code */
},
byId('upload').onclick = function(){
/* a bunch of code */
};
// rest of the code
};

Categories

Resources