3

The following code (inspired by the main answer of HTML5 Pre-resize images before uploading) takes the selected image file from the <input type="file">, recompresses it to a JPG with quality 50% into the variable dataurl.

Question: How to replace the content of the <input type="file"> by this newly-compressed file? So that when submitting the <form>, it's this new file which will be submitted?

Note: I don't want to use AJAX for the upload, but the standard POST of the <form>. The "posted" file should be dataurl, i.e. it should be as if the file selected by the user in the <input type="file"> were the file generated by dataurl.

function doit() {
    var input = document.getElementById('file'),
        canvas = document.getElementById('canvas');
    var file = input.files[0];
    var img = document.createElement("img");
    var reader = new FileReader();  
    reader.onload = function(e) { img.src = e.target.result }
    reader.readAsDataURL(file);
    var ctx = canvas.getContext("2d");
    ctx.drawImage(img, 0, 0);
    var dataurl = canvas.toDataURL("image/jpeg", 0.5);
    console.log(dataurl);
}    
<form action="/upload" method="post">
<input type="file" onchange="doit();" id="file" /><br><br>
<input type="submit" />
</form>
<canvas id="canvas" style="display: none" />  
Basj
  • 41,386
  • 99
  • 383
  • 673
  • 1
    You cannot add dynamically created Blob/File objects to an HTML form, you could set an `` to a base64 encoded string of the file contents instead if AJAX cannot be used – Patrick Evans Jun 26 '18 at 23:45
  • @PatrickEvans [now you can](https://stackoverflow.com/questions/47119426/how-to-set-file-objects-and-length-property-at-filelist-object-where-the-files-a/47172409#47172409) (currently only in Chrome and upcoming FF62). – Kaiido Jun 27 '18 at 00:19
  • Why don't you want to use AJAX? That's the proper way. Beside, [your compression thing is flawn.](https://stackoverflow.com/questions/48632459/downsizing-image-dimensions-via-pure-js-leads-to-image-size-inflation-in-bytes/48634519#48634519) – Kaiido Jun 27 '18 at 00:20
  • Unless you use FormData (ajax) it's not possible to put your custom stuff inside $_FILES. If you happen to use ajax [this](https://stackoverflow.com/a/45698370/6160662) will help. – Vinay Jun 27 '18 at 02:29
  • @Kaiido I don't really agree with the answer you linked. The compression thing is not flawn. Sometimes (example: you have a 4MB JPG as input) you have no other other choice than reencoding to JPG with a lower quality factor (e.g. 60%) to get an output which is, say, < 500 KB. When doing this, we must remember that reenconding something already lossy-compressed will add more artefacts, but we often have no other choice. What other option do you see to get a max-500KB file if I give you a 4 MB JPEG? :) – Basj Jun 27 '18 at 12:23
  • @Kaiido Thanks for your first link, seems interesting! – Basj Jun 27 '18 at 12:25
  • @Basj as I replied there what the linked answer says is that the Canvas API is not meant to do this. Use an other tool, which won't care taking 3 times more time to apply a better compression scheme. – Kaiido Jun 27 '18 at 12:28
  • @PatrickEvans: or maybe an `` that gets filled with `document.getElementById('jpgcontent').value = dataurl;`? Its content seems to be `data:image/jpeg;base64,/9j/4AA...` – Basj Jun 28 '18 at 07:22
  • 1
    Yes, that would work too. And would just need to cut the `data:image/jpeg;base64,` part and decode on server side – Patrick Evans Jun 28 '18 at 11:33
  • @PatrickEvans as this was your idea, would you like to post an answer? – Basj Jun 28 '18 at 20:03

1 Answers1

2

It seems we cannot modify the <input type="file">, but we can add the data to another text field (as advised by @PatrickEvans) or an <input type="hidden">:

function doit() {
    var file = document.getElementById('file').files[0],
        canvas = document.getElementById('canvas'),
        hidden = document.getElementById('hidden'),
        ctx = canvas.getContext("2d"),
        img = document.createElement("img"),
        reader = new FileReader();  
    
    reader.onload = function(e) { 
        img.src = e.target.result;
    }
    
    img.onload = function () { 
        ctx.drawImage(img, 0, 0);
        hidden.value = canvas.toDataURL("image/jpeg", 0.5);
    }
    reader.readAsDataURL(file);
}
<input type="file" onchange="doit();" id="file" />

<form action="/upload" method="post">
<input type="hidden" id="hidden" />
<input type="submit" />
</form>

<canvas id="canvas" style="display: none" />

The output hidden field in the <form> is base64-encoded, i.e. something like:

data:image/jpeg;base64,/9j/4AAQSkZJRgAB...
Basj
  • 41,386
  • 99
  • 383
  • 673