Unnecessarily large images can lead to slower response times, consume excessive bandwidth, and provide a sluggish experience for users, especially those on slower connections. This can result in higher bounce rates or fewer conversions.
Compressing images before you upload them can mitigate these issues and provide a better user experience. The Sharp module makes this process quick and easy.
Setting Up Your Development Environment
To demonstrate the process of compressing images, start by setting up an image upload service using multer. You can speed up the process by cloning this GitHub repository.
After cloning the GitHub repository, run this command to install the dependencies for the image upload service:
npm install
Next, install Sharp by running this command:
npm install sharp
The Sharp module is a high-performance Node.js library for processing and manipulating images. You can use Sharp to resize, crop, rotate, and perform various other operations on images efficiently. Sharp also has excellent support for compressing images.
Compression Techniques for Different Image Formats
Different image formats have distinct compression techniques. This is because they each cater to specific uses and requirements, and they prioritize different factors including quality, file size, and features like transparency and animations.
JPG/JPEG
JPEG is an image compression standard developed by the Joint Photographic Experts Group to compress photographs and realistic images with continuous tones and color gradients. It uses a lossy compression algorithm, generating smaller files by discarding some image data.
To compress a JPEG image with Sharp, import the Sharp module and pass the filePath or a buffer of the image as an argument. Next, call the .jpeg method on the Sharp instance. Then, pass a configuration object with a quality property that takes a number between 0 and 100 as a value. Where 0 returns the smallest compression with the lowest quality and 100 returns the biggest compression with the highest quality.
You can set the value depending on your needs. For best compression results, keep the value between 50-80 to strike a balance between size and quality.
Finish up by saving the compressed image to the file system using the .toFile method. Pass the path of the file you want to write to as an argument.
For example:
await sharp(req.file.path)
.jpeg({ quality: 60 })
.toFile(`./images/compressed-${req.file.filename}`)
.then(() => {
console.log(`Compressed ${req.file.filename} successfully`);
});
The default value for quality is 80.
PNG
PNG (Portable Network Graphics) is an image file format known for its lossless compression and support for transparent backgrounds.
Compressing a PNG image with Sharp is similar to compressing a JPEG image with Sharp. However, there are two changes you need to make when the image is in PNG format.
- Sharp processes PNG images using the .png method instead of the .jpeg method.
- The .png method uses compressionLevel, which is a number between 0 and 9 instead of quality in its configuration object. 0 gives the fastest and largest compression possible, while 9 gives the slowest and smallest compression possible.
For example:
await sharp(req.file.path)
.png({
compressionLevel: 5,
})
.toFile(`./images/compressed-${req.file.filename}`)
.then(() => {
console.log(`Compressed ${req.file.filename} successfully`);
});
The default value for compressionLevel is 6.
Other Formats
Sharps supports compression in various other image formats which include:
- WebP: WebP image compression with Sharp follows the same process as JPG. The only difference is that you have to call the webp method instead of the .jpeg method on the Sharp instance.
- TIFF: TIFF (Tag Image File Format) image compression with Sharp follows the same process as JPG. The only difference is that you have to call the tiff method instead of the .jpeg method on the Sharp instance.
- AVIF: AVIF (AV1 Image File Format) image compression with Sharp follows the same process as JPG. The only difference is that you have to call the avif method instead of the .jpeg method on the Sharp instance. The AVIF default value for quality is 50.
- HEIF: HEIF (High Efficiency Image File Format) image compression with Sharp follows the same process as JPG. The only difference is that you have to call the heif method instead of the .jpeg method on the Sharp instance. The AVIF default value for quality is 50.
Compressing Images With Sharp
If you cloned the GitHub repository, open your app.js file and add the following imports.
const sharp = require("sharp");
const { exec } = require("child_process");
exec is a function provided by the child_process module that allows you to execute shell commands or external processes from your Node.js application.
You can use this function to run a command that compares the file sizes before and after compression.
Next, replace the POST ‘/single’ handler with the code block below:
app.post("/upload-and-compress", upload.single("image"), async (req, res) => {
try {
if (!req.file) {
return res.status(404).send("Please upload a valid image");
}
const compressedFileName = req.file.filename.split(".")[0];
const compressedImageFilePath = `./images/${compressedFileName}-compressed.png`;
await sharp(req.file.path)
.jpeg({ quality: 50 })
.toFile(compressedImageFilePath)
.then(() => {
let sizeBeforeCompression, sizeAfterCompression;
const sizeBeforeCompressionCommand = `du -h ${req.file.path}`;
const sizeAfterCompressionCommand = `du -h ${compressedImageFilePath}`;
exec(sizeBeforeCompressionCommand, (err, stdout, stderr) => {
sizeBeforeCompression = stdout.split("\\t")[0];
exec(sizeAfterCompressionCommand, (err, stdout, stderr) => {
sizeAfterCompression = stdout.split("\\t")[0];
res.send({
message: "Image uploaded and compressed successfully",
sizeBeforeCompression,
sizeAfterCompression,
});
});
});
});
} catch (error) {
console.log(error);
}
});
The code block above implements the technique for compressing JPEG images and compares the sizes before and after using the du command.
The du command is a Unix utility that stands for "disk usage." It estimates file space usage and analyzes disk usage in a directory or set of directories. When you run the du command with the -h flag, it displays the file space each subdirectory uses and its contents in human-readable form.
Start up your upload service by running this command:
node app.js
Next, test your application by sending a JPG image to the route localhost:<PORT>/upload-and-compress using the Postman client app or any other API testing tool.
You should see a response similar to this one:
{
"message": "Image uploaded and compressed successfully",
"sizeBeforeCompression": "13M",
"sizeAfterCompression": "3.1M"
}
Other Uses of the Sharp Module
In addition to compressing images, the sharp module can also resize, crop, rotate, and flip images to desired specifications. It also supports color space adjustments, alpha channel operations, and format conversions.