I am trying to make an upload script which resizes multiple images client side before the saveimage.php handles them. The reason for this is because they will be upload
JavaScript loads image asynchronously. That means once it is assigned a new image's .src it will begin loading the image but will simultaneously continue processing the javascript after the .src while the image takes time to load.
You're using the same image variable (var img
) inside your for loop. Therefore, each time through the loop you are overwriting the previous img before the previous image has been fully loaded.
Here's an image loader that fully loads all image and then calls the start() function where you can do you processing:
// image loader
// put the paths to your images in imageURLs[]
var imageURLs=[];
imageURLs.push("myImage1.png");
imageURLs.push("myImage2.png");
// etc, etc.
// the loaded images will be placed in imgs[]
var imgs=[];
var imagesOK=0;
loadAllImages(start);
function loadAllImages(callback){
for (var i=0; i<imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function(){
imagesOK++;
if (imagesOK>=imageURLs.length ) {
callback();
}
};
img.onerror=function(){alert("image load failed");}
img.crossOrigin="anonymous";
img.src = imageURLs[i];
}
}
function start(){
// the imgs[] array now holds fully loaded images
// the imgs[] are in the same order as imageURLs[]
}
I had kind of the same issue, but I got to manage that by first displaying all the images in a hidden "img" tag taking as a reference this post "HTML5 to display uploaded image untill next one is uploaded?" and after that I trigger a function with an additional button that uses the recent created images and draw all of them in an separated canvas that were also created at the same time that the images where uploading, the code will be like this
the html:
<form>
<input type="file" id="files" name="files[]" multiple />
<button type="button" id="run">Run</button>
</form>
<div id="preview">
<output id="list"></output><br>
<output id="list2"></output><br>
<div id="status"></div>
</div>
The script:
document.getElementById('files').addEventListener('change', handleFileSelect,false);
document.getElementById('run').addEventListener('click', read);
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img class="thumb" name="forms" src="', e.target.result, '" title="', escape(theFile.name), '"/ hidden>'].join('');
document.getElementById('list').insertBefore(span, null);
var span = document.createElement('span');
span.innerHTML = ['<canvas></canvas>'].join('');
document.getElementById('list2').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
function read() {
imgs = document.getElementsByName('forms');
canvas = document.getElementsByTagName("canvas");
for (var i = 0; i < imgs.length; ++i) {
ctx[i] = canvas[i].getContext("2d");
ctx[i].clearRect(0, 0, canvas[i].width, canvas[i].height);
preview.style.width = imgs[i].width + "px";
preview.style.height = imgs[i].height + "px";
canvas[i].width = imgs[i].width;
canvas[i].height = imgs[i].height;
ctx[i].drawImage(imgs[i], 0, 0, imgs[i].width, imgs[i].height);
// do all you need with each new canvas canvas
}
}
In the onload callback use the this
keyword(instead of img
) to for the current image, img
may/will be overwritten in the for loop before the callback fires.
img.height --> this.height
etc
Also is there a reason you're using a custom content type instead of application/x-www-form-urlencoded
?
Url encoded form data is much easier to work with.
I have struggled with the same problem for some time now, but I finally cobbled together another solution, using bits of code from others, who have addressed this problem, and it works. It may not be perfect, and I would welcome any improvement suggestions. The concept is that you have two Javascript Functions, which are identicle called Resize0 and Resize1, and they call each other until all photos have been resized then the Function which resizes the last image Submits the Form. The two Functions pass parameters between each other so that one of them will know when to Submit the Form. Two of those parameters are, Number_Of_Images and current Image_Number. You can also download a working Example Web page from http://www.wantitconsulting.co.nz/ExampleResizeUpload.zip . You will need to create a folder in the base directory of your test web site for the Images to go into. The foldername is TestResizeUpload. The Server side uses php.