← Back to posts

Optimizing images with Node.js

·Read in Portuguese

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 .env file, so I can access them using process.env.VARIABLE_NAME.
  • const sharp = require('sharp') imports the sharp library
  • const fs = require('fs').promisesfs already comes built into Node, so here I use promises so I can use async/await instead 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 the SRC_FOLDER environment variable, which is where the "large" images to be optimized should be.
  • const pattern = new RegExp('^.*.(jpg|JPG|gif|GIF|png|PNG)$') — I only want jpg, gif, or png images. (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();
  • resize resizes the image; here you can use the size defined in the environment variables.
  • toFormat always converts the image to jpg.
  • toBuffer returns the image content as a Buffer.

Example

Image before being optimized.

Image before optimizing

Image after being optimized.

Image after optimizing

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!

Optimizing images with Node.js · Programming and Design | Gabriel Asakawa