JavaScript image compression and resizing

Uploading and downloading images is a very common feature in modern web applications but exchanging files between client and server can quickly become a high resource consuming task. We must also consider that most Internet traffic comes from mobile devices, so we can expect users to upload photos taken with their phones. Those files can be very heavy (> 10MB) because of the ever increasing camera resolution on new mobile devices.

Photo by Kyle Hinkson on Unsplash

Sharing images in your platform means that users upload their photos to your storage server and then other users download those photos to use them somehow. This task involves much more resources compared to storing a new record in the database. We can expect an higher cost in terms of:

  • Upload bandwidth.
  • Download bandwidth. In a typical use case, there are many downloads for each image uploaded.
  • Storage. Photos and files are typically stored in a disk or in some object storage service. It’s important to note that once you save a photo in your storage you must keep it stored throughout the lifetime of the software unless you apply some deletion policy. For this reason storage costs always increase during time, in contrast to bandwidth costs that depend on current usage.

Due to the pandemic emergency for COVID 19, in the period between March and June 2020, Nuvola has become the main hub for teachers, pupils and parents. This situation results in a rapid increase in traffic, as we have already talked about in a previous article. Furthermore, the needs of schools have changed to address distance learning. For example, students should send homework to teachers and teachers should send corrections back. Until now, this functionality was not needed because this process was done physically in the classroom. This new feature clearly implies file sharing. Talking with our customers we discovered that users prefer to do their homework in their exercise book, take a picture and share it on the platform. This means that most of the shared files are images and, for this reason, the benefit of image compression will be really huge.

How can image sharing be optimized?

The obvious answer is image compression. However, if image quality is your software primary concern, this technique is probably not right for you. A common solution involves server-side compression, reducing download bandwidth and storage space required. However this approach leads to increased CPU cycles which means an additional cost, even though probably less expensive than download bandwidth.

Thanks to modern browser API we can also reduce unnecessary upload bandwidth compressing images client-side, before uploading them. Reducing bandwidth also means a faster upload because compression time is much smaller than a large file upload request over network.

HTML5 features such as Canvas, FileReader and Blob allow compressing images directly in the browser, resulting in a lower number of bytes the platform needs to upload, store and download.

A little bit of context from MDN

The Canvas API provides a means for drawing graphics via JavaScript and the HTML <canvas> element. Among other things, it can be used for animation, game graphics, data visualization, photo manipulation, and real-time video processing. The Canvas API largely focuses on 2D graphics. The WebGL API, which also uses the <canvas> element, draws hardware-accelerated 2D and 3D graphics.

The FileReader object lets web applications asynchronously read the contents of files (or raw data buffers) stored on the user’s computer, using File or Blob objects to specify the file or data to read. File objects may be obtained from a FileList object returned as a result of a user selecting files using the <input> element, from a drag and drop operation’s DataTransfer object, or from the mozGetAsFile() API on an HTMLCanvasElement.

The Blob object represents a blob, which is a file-like object of immutable, raw data; they can be read as text or binary data, or converted into a ReadableStream so its methods can be used for processing the data. Blobs can represent data that isn’t necessarily in a JavaScript-native format. The File interface is based on Blob, inheriting blob functionality and expanding it to support files on the user’s system.

Image compression steps

  1. Read the file using an <input> element with type=”file”
  2. Create a Blob with the file data and get its URL with createObjectURL
  3. Create an helper image object and use the blob URL as source
  4. Use the onload callback to process the image
  5. Create a canvas element setting the width and height to match the new dimensions of the image.
  6. Create a 2D context object and draw the image on the canvas
  7. Export the image with the desired output quality

    mimeType is the mime type of the result image, like image/jpeg, image/png . Value of quality ranges from 0 to 1 and represents the quality of the output image. If you don’t specify the mime and quality in the toBlob() method then default quality will be set and the mime type will be image/png. HTMLCanvasElement.toBlob is not fully supported by all browsers, see the polyfill section below.
  8. (Optional) Show the compressed image in the document

Polyfill canvas.toBlob

A low performance polyfill based on toDataURL.

Source: https://developer.mozilla.org/it/docs/Web/API/HTMLCanvasElement/toBlob

Final code

Try to upload an image to see how the resizing and compression steps works.

See the Pen
JS image resizer
by Mirco Bellagamba (@mirco-bellagamba)
on CodePen.

Default image
Mirco Bellagamba
Mirco Bellagamba is a software engineer dealing with front-end web development and mobile applications. Software architectures, modern web technologies, microservices are some of the IT topics he is most interested in.

Leave a Reply

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.