Search code examples
javascriptimageimagejpeg

JS-Convert an Image object to a jpeg file


So, I have a user input which serve to upload pictures. This is a simple example:

function handleImage(e){
    var reader = new FileReader();
    reader.onload = function(event){
        var img = new Image();
        img.onload = function(){
            console.log (img);
        }
        img.src = event.target.result;
    }
    reader.readAsDataURL(e.target.files[0]);     
}
<input type="file" onchange="handleImage(event)"><br>

As you can see, I display an Image () on the console. I want to convert this Image into a jpg file.

I understood how to get the pixels of the picture but it's completely crazy to send it to a server: It's to large!

I also tried to access to the jpg file stored in the computer but I do not achieve to anything.

The only way I found is to send it with a form like this:

<form action="anything.php" method="post" enctype="multipart/form-data">
    <input type="file" name="fileToUpload" id="fileToUpload">
</form>   

And in PHP:

$_FILES["fileToUpload"]["tmp_name"]

Why not with JS?

My final goal is to send the jpg file with AJAX.

Tell me if you have some questions.


Solution

  • The simplest way is to use a canvas element and then invoke a download action allowing the user to select where to save the image.

    You mention that the image is large, but not how much - be aware that with canvas you will also run into restrictions when the image source starts to touch around the 8k area in pixel size.

    A simplified example (IE will require a polyfill for toBlob()).:

    • Load image source via input
    • Use File blob directly as image source via URL.createObjectURL()
    • When loaded, create a temporary canvas, set canvas size = image size and draw in image
    • Use toBlob() (more efficient on memory and performance and require no transcoding to/from Base64) to obtain a Blob.
    • We'll convert the Blob to File (a subset object and it will reference the same memory) so we can also give a filename as well as (important!) a binary mime-type.

    Since the mime-type for the final step is binary the browser will invoke a Save as dialog.

    document.querySelector("input").onchange = function() {
      var img = new Image;
      img.onload = convert;
      img.src = URL.createObjectURL(this.files[0]);
    };
    
    function convert() {
      URL.revokeObjectURL(this.src);             // free up memory
      var c = document.createElement("canvas"),  // create a temp. canvas
          ctx = c.getContext("2d");
      c.width = this.width;                      // set size = image, draw
      c.height = this.height;
      ctx.drawImage(this, 0, 0);
      
      // convert to File object, NOTE: we're using binary mime-type for the final Blob/File
      c.toBlob(function(blob) {
        var file = new File([blob], "MyJPEG.jpg", {type: "application/octet-stream"});
        window.location = URL.createObjectURL(file);
      }, "image/jpeg", 0.75);  // mime=JPEG, quality=0.75
    }
    
    // NOTE: toBlob() is not supported in IE, use a polyfill for IE.
    <label>Select image to convert: <input type=file></label>

    Update: If you just are after a string (base-64 encoded) version of the newly created JPEG simply use toDataURL() instead of toBlob():

    document.querySelector("input").onchange = function() {
      var img = new Image;
      img.onload = convert;
      img.src = URL.createObjectURL(this.files[0]);
    };
    
    function convert() {
      URL.revokeObjectURL(this.src);             // free up memory
      var c = document.createElement("canvas"),  // create a temp. canvas
          ctx = c.getContext("2d");
      c.width = this.width;                      // set size = image, draw
      c.height = this.height;
      ctx.drawImage(this, 0, 0);
      
      // convert to File object, NOTE: we're using binary mime-type for the final Blob/File
      var jpeg = c.toDataURL("image/jpeg", 0.75);  // mime=JPEG, quality=0.75
      console.log(jpeg.length)
    }
    <label>Select image to convert: <input type=file></label>