Optimizing images with Node.js
I was working on a personal project and ran into the need to optimize the size of the images I had to use. There are several online services for this (resizing, cropping, reducing file size, etc.), but I found a pretty interesting JavaScript library. It's called sharp, and I want to share with you how I solved my problem.
The code is quite simple yet powerful :) (you can clone it from GitHub).
require('dotenv/config');
const sharp = require('sharp');
const fs = require('fs').promises;
const compress = async () => {
const directory = await fs.readdir(process.env.SRC_FOLDER);
const pattern = new RegExp('^.*.(jpg|JPG|gif|GIF|png|PNG)$');
const files = directory.filter(file => pattern.test(file));
if (files.length > 0) {
// `async`/`await` doesn't work inside Array functions, so `await Promise.all` is needed here, since the `sharp` function being used returns a promise.
await Promise.all(
files.map(async file => {
const content = await fs.readFile(`${process.env.SRC_FOLDER}/${file}`);
const compressContent = await sharp(content)
.resize(Number(process.env.WIDTH), Number(process.env.HEIGHT), {
fit: 'inside',
withoutEnlargement: true,
})
.toFormat('jpeg', {
progressive: true,
quality: 90,
})
.toBuffer();
await fs.writeFile(
`${process.env.DEST_FOLDER}/${file}`,
compressContent,
);
}),
);
}
};
compress();So let's break it down?
require('dotenv/config');
const sharp = require('sharp');
const fs = require('fs').promises;require('dotenv/config')— since I use some environment variables to make things more customizable, the dotenv library loads the environment variables from the.envfile, so I can access them usingprocess.env.VARIABLE_NAME.const sharp = require('sharp')imports thesharplibraryconst fs = require('fs').promises—fsalready comes built into Node, so here I usepromisesso I can useasync/awaitinstead of traditional callbacks.
The compress function is the one that does the heavy lifting, so let's see how it works.
const directory = await fs.readdir(process.env.SRC_FOLDER);
const pattern = new RegExp('^.*.(jpg|JPG|gif|GIF|png|PNG)$');
const files = directory.filter(file => pattern.test(file));const directory = await fs.readdir(process.env.SRC_FOLDER)— here we read the directory specified in theSRC_FOLDERenvironment variable, which is where the "large" images to be optimized should be.const pattern = new RegExp('^.*.(jpg|JPG|gif|GIF|png|PNG)$')— I only wantjpg,gif, orpngimages. (You can add other extensions if you want.)const files = directory.filter(file => pattern.test(file))— the directory might contain other types of files besides images, so I filter the files based on the extensions I actually want to process.
Here we use the sharp library's functions
const compressContent = await sharp(content)
.resize(Number(process.env.WIDTH), Number(process.env.HEIGHT), {
fit: 'inside',
withoutEnlargement: true,
})
.toFormat('jpeg', {
progressive: true,
quality: 90,
})
.toBuffer();resizeresizes the image; here you can use the size defined in the environment variables.toFormatalways converts the image tojpg.toBufferreturns the image content as aBuffer.
Example
Image before being optimized.

Image after being optimized.

Well, that's it folks, I hope this helps if you have a similar need. If you have questions, compliments, or suggestions, leave a comment. If you enjoyed the content, share it with your friends.
See you next time!