Compress an Image File in the Browser with JavaScript

OpenJavaScript 0

Last updated: December 28, 2022.

With help from the HTML5 Canvas API, you can do image compression on the client-side with JavaScript.

This is useful, for example, when wanting to reduce the size of an image before upload. It can also be utilized to create a client-side image compression app.

The following code supports the compression of an image to webp or jpeg format and outputs the result to the DOM:

const inputEl = document.querySelector('input');    

inputEl.addEventListener('change', () => {
// Function fires when new image is selected by user

  const file = inputEl.files[0];
  const blob = new Blob([file], { type: file.type });
  // Gets user-selected image and converts to blob

  const url = URL.createObjectURL(blob);
  // Creates temporary URL to image blob in browser memory

  const img = new Image();
  img.src = url;
  // Creates new image element and sets src to temp URL

  img.onload = () => {

    document.body.appendChild(img);
    // Appends loaded image (original) to DOM 

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    // Creates new canvas

    canvas.width = img.naturalWidth;
    canvas.height = img.naturalHeight;
    // New canvas should be natural width of uploaded image

    ctx.drawImage(img, 0, 0);
    // Draw image to canvas

    canvas.toBlob((newBlob) => {
    // Converts canvas to new blob

      const fr = new FileReader();
      fr.readAsDataURL(newBlob);
      // Creates data URL for new image blob

      fr.addEventListener('load', () => {

        const newImg = new Image();
        const dataURL = fr.result;
        newImg.src = dataURL;
        // Creates new image element with src set to new image (data) URL

        newImg.onload = () => {
          document.body.appendChild(newImg);
          // Appends new image to DOM
        }
      })

    },'image/webp', 0.7); 
    // Sets type (image/webp OR image/jpeg) and quality (0-1)

  }
})

Uploading the compressed image

The advantage of having the compressed image stored in a data URL is that you have the flexibility to upload the file as a pure string to the server, as part of a FormData object (as below) or as an image blob.

In the example below, the payload is sent to a live test API (httpbin.org):

inputEl.addEventListener('change', () => {

  const file = inputEl.files[0];
  const blob = new Blob([file], { type: file.type });

  const url = URL.createObjectURL(blob);

  const img = new Image();
  img.src = url;
  img.onload = () => {

    document.body.appendChild(img);    

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    canvas.width = img.naturalWidth;
    canvas.height = img.naturalHeight;

    ctx.drawImage(img, 0, 0);

    canvas.toBlob((newBlob) => {

      const fr = new FileReader();
      fr.readAsDataURL(newBlob);

      fr.addEventListener('load', () => {
        const newImg = new Image();
        const dataURL = fr.result;
        newImg.src = dataURL;

        newImg.onload = () => {

          document.body.appendChild(newImg);

          const fd = new FormData();
          fd.append('compressed-image', dataURL);
          // Creates new FormData object and appends dataURL

          fetch('http://httpbin.org/post', {
          method: "POST",
          body: fd, // OR 'newBlob' for Blob
          })
          .then(res => res.json())
          .then(data => console.log(data)) // Logs server response to console

        }
      })

    },'image/webp', 0.7);

  }
})

Making compressed image available as a download

Another option is to allow the user to download the compressed image.

For this, you'll probably want to implement a UI so that a user can see the original and compressed image before deciding to download.

The code below also displays each image type and filie size:

<main>
  <input type="file">
  <div class="image-container">
    <div id="original">
      <img>
      <div></div>
    </div>
    <div id="compressed">
      <img>
      <div></div>
    </div>
  </div>
</main>

</body>
<script>

const inputEl = document.querySelector('input');    

inputEl.addEventListener('change', () => {

  document.querySelectorAll('img').forEach((img) => {
    img.removeAttribute('src');
  })

  const file = inputEl.files[0];
  const blob = new Blob([file], { type: file.type });

  const url = URL.createObjectURL(blob);

  const img = document.querySelector('#original img');
  img.src = url;
  // Sets src of original image element in DOM to URL to image in browser memory

  img.onload = () => {

    document.querySelector('#original div').innerHTML = `
      

${blob.type}, ${(blob.size).toLocaleString('en-US')} KB

`; // Prints type and size information from blob const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; ctx.drawImage(img, 0, 0); canvas.toBlob((newBlob) => { const fr = new FileReader(); fr.addEventListener('load', () => { const newImg = document.querySelector('#compressed img'); const dataURL = fr.result; newImg.src = dataURL; // Sets src for compressed image element to redrawn image now as data URL newImg.onload = () => { document.querySelector('#compressed div').innerHTML = ` <p>${newBlob.type}, ${(newBlob.size).toLocaleString('en-US')} KB</p> <a href="${dataURL}" download="${file.name.split(".").unshift()}_compressed"> <button>Download</button> </a> `; // Gets type and size from newBlob and create download element } }) fr.readAsDataURL(newBlob); },'image/webp', 0.7); } }) </script>

Related posts