Compress an Image File in the Browser with JavaScript
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
- Read a File into JavaScript using FileReader
- What is a Blob object in JavaScript?
- Image URL to Blob in JavaScript
- POST form data using JavaScript’s Fetch API
- Create a client-side download with JavaScript