JS-Convert an Image object to a jpeg file

SphynxTech picture SphynxTech · Dec 20, 2017 · Viewed 17.3k times · Source

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

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.

Answer

user1693593 picture user1693593 · Dec 20, 2017

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>