I have an interface that allows multiple file uploads. The user dumps a set of files on the file element.
The script then sets the height, width and dpi of those images and displays them in a large thumbnail.
That all works fine. If I right-click and save an image, it is now 1600×1600 at 96 dpi. Exactly what I specified.
Now, I need to upload the images through AJAX by creating a new FileList. But how do I specify the filename and the content for the file list?
I have a hidden file element which I’ll update with the new file list which will trigger the AJAX.
Here is the HTML and the javascript/jquery:
<form enctype="multipart/form-data" id="frmUploadImage">
<input name="files" type="file" id="imgUpload" class="upload" multiple accept="image/*"/>
</form>
<form enctype="multipart/form-data" id="frmSaveImage" style="display:none">
<input name="files" type="file" id="saveImages" class="upload" multiple accept="image/*"/>
</form>
<input type="button" id="saveIt" value="Save">
<script>
$(`#imgUpload`).on(`change`, function() {
const $fileUpload = $(this);
Array.from($fileUpload.get(0).files).forEach(function(f, i) {
$(`#compressResultsContainer`).append(`<div id="image_${i}_container" class="compressedImageContainer"><img id="source_${i}" style="display:none"/><img id="image_${i}" class="compressedImage" style="width: 200px; height:200px"/><canvas id="canvas_${i}" style="display:none"></canvas><div>`);
let $preview = $(`#source_${i}`);
var reader = new FileReader();
reader.addEventListener(`load`, function(e) {
$preview.attr(`src`, e.target.result);
$preview.on(`load`, function() {
console.log(`loading preview ${i}`);
compressFile(this, i);
});
}, false);
if (f) {
reader.readAsDataURL(f);
}
});
});
function compressFile(loadedData, imgNum) {
var mime_type = `image/jpeg`;
var cvs = document.getElementById(`canvas_${imgNum}`);
cvs.width = 1600;
cvs.height = 1600;
var ctx = cvs.getContext(`2d`).drawImage(loadedData, 0, 0, 1600,1600);
var newImageData = cvs.toDataURL(mime_type, 96);
var result_image_obj = new Image();
result_image_obj.src = newImageData;
$(`#image_${imgNum}`).attr(`src`, result_image_obj.src);
}
// my test function just to see if I can build new form data---
$(`#saveIt`).on(`click`, function() {
let formData = new FormData();
$(`.newCanvas`).each(function() {
let file;
const canvas =document.getElementById($(this).attr(`id`));
canvas.toBlob((blob) => {
console.log(`canvas:`, $(this).attr(`id`));
file = new File([blob], $(this).attr(`filename`), { type: `image/jpeg` });
formData.append(`savedImages`, new File([blob], $(this).attr(`filename`), { type: `image/jpeg` }));
console.log(file);
}, `image/jpeg`);
});
setTimeout(function() { console.log(formData); }, 3000);
});
</script>
I’m basically stuck on how to populate the new FileList with the proper content — the compressed images and their filename.
2
Answers
I finally had time to get back to this yesterday. This works:
You had ONE tiny issue which made nothing to work: looping through the
.newCanvas
elements when absolutely no element had the class. So the loop squarely didn’t run.There also is a couple "improvements" that can be done… But to have the
formData
populated, that was the only thing.So I added
class="newCanvas"
to<canvas id="canvas_${i}"
on that line:Notice that I also added a
data-name
to hold the file name. That information has to be carried by thecanvas
to later be usable in the.each()
loop.Now about a couple improvements:
You use backticks everywhere. Good practice would be to use them when really needed, like when some templating
${variable}
is inside the string…document.getElementById($(this).attr(
id))
is EXACTLY the same as justthis
.Except that you’ve made a jQuery lookup on
this
to get theid
and then used thatid
to retreive the element using plain JS.In an
.each()
loop,this
is the element of the current loop.Within the
.toBlob()
callback,$(this)
no longer is thecanvas
element. You have to get the informations from the canvas before calling.toBlob()
You used a
setTimeout()
to wait for thecanvas.toblob()
results… That is not reliable. I suggest you to use the number of appended files in theformData
and compare that with the number of.newCanvas
element to process.That comparison has to be in the
canvas.toblob()
callback.So here is the code I suggest you to try.