DEV Community

Cover image for Image Compression with Node.js
Francisco Mendes
Francisco Mendes

Posted on

Image Compression with Node.js

Currently, I feel that the market is increasingly oriented towards the use of third-party services to make part of our pipeline. One of the most common is to compress images.

However, I will show that with a simple Express.js api we can upload an image, change the Mime Type and still decrease its size.

I don't think I need to talk about Express.js because everyone who uses Node.js has used this framework at some point. Today I'm going to focus on multer and sharp.

Nowadays uploading images is one of the most basic operations of all applications. Multer is a middleware for handling multipart/form-data that is mainly used to upload files. By combining Express.js with multer, we will be able to easily implement the file upload feature.

Sharp is a module for Node.js to convert images of the most diverse formats and varied dimensions to a smaller size, without having to worry about the color space, channels and alpha transparency, because all of these are treated correctly.

The image format that we are going to use in this example is WebP, which offers compression greater than png and jpg, which helps to load web pages more quickly. And it is supported by all browsers.

How to use

First we will create a new project in Node.js and then we will install the necessary dependencies:

# We will use the default values.
npm init -y

# Necessary dependencies.
npm install express multer sharp
Enter fullscreen mode Exit fullscreen mode

After that we will create a simple api:

const express = require("express");

const app = express();

app.get("/", (req, res) => {
 return res.json({ message: "Hello world 🔥🇵🇹" });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Once the application is working properly, we will move on to configuring multer. If you visited the multer documentation, I believe you saw that we can save the images in two ways: DiskStorage or MemoryStorage.

In this case, we will use MemoryStorage, because we want to have access to the buffer that is made available by multer. And we will also use the static function of Express.js so that later we can serve our images.

const express = require("express");
const multer = require("multer");

const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage });

app.use(express.static("./uploads"));

app.get("/", (req, res) => {
 return res.json({ message: "Hello world 🔥🇵🇹" });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

After that, we will create an endpoint to make the http request with the POST verb, however we will add the middleware upload and we will only upload a single file which we will call “picture”.

app.post("/", upload.single("picture"), async (req, res) => {
 // The logic goes here.
});
Enter fullscreen mode Exit fullscreen mode

The next step is to check if the "uploads" folder exists in our workspace, if it doesn't, we will want Node.js to create it for us. For that we need to have access to the file system, so we will use the fs module.

app.post("/", upload.single("picture"), async (req, res) => {
 fs.access("./uploads", (error) => {
   if (error) {
     fs.mkdirSync("./uploads");
   }
 });
 // Even more logic goes here.
});
Enter fullscreen mode Exit fullscreen mode

Now we can start working with sharp, but first I like to create a random string to have it before the name of the image, this is because several images can have the same name and so it is better to be careful. In this case I am using a timestamp (to make it easier to understand) but the ideal was to have a random string of 16 characters. Then we will access the buffer and the originalname of the image thanks to multer.

app.post("/", upload.single("picture"), async (req, res) => {
 fs.access("./uploads", (error) => {
   if (error) {
     fs.mkdirSync("./uploads");
   }
 });
 const { buffer, originalname } = req.file;
 const timestamp = new Date().toISOString();
 const ref = `${timestamp}-${originalname}.webp`;
 // Even more logic goes here.
});
Enter fullscreen mode Exit fullscreen mode

Now just pass the image buffer to sharp and then configure the desired quality, the format we want and where the file goes. In this case, I will want the file in the “uploads” folder with the name we have assigned (the ref variable).

app.post("/", upload.single("picture"), async (req, res) => {
 fs.access("./uploads", (error) => {
   if (error) {
     fs.mkdirSync("./uploads");
   }
 });
 const { buffer, originalname } = req.file;
 const timestamp = new Date().toISOString();
 const ref = `${timestamp}-${originalname}.webp`;
 await sharp(buffer)
   .webp({ quality: 20 })
   .toFile("./uploads/" + ref);
 // Almost finished...
});
Enter fullscreen mode Exit fullscreen mode

Last but not least, I will create a variable called link that will be the url with which we will be able to view our new image in the browser.

The final code should be as follows:

const express = require("express");
const multer = require("multer");
const sharp = require("sharp");
const fs = require("fs");

const app = express();
const storage = multer.memoryStorage();
const upload = multer({ storage });

app.use(express.static("./uploads"));

app.get("/", (req, res) => {
  return res.json({ message: "Hello world 🔥🇵🇹" });
});

app.post("/", upload.single("picture"), async (req, res) => {
  fs.access("./uploads", (error) => {
    if (error) {
      fs.mkdirSync("./uploads");
    }
  });
  const { buffer, originalname } = req.file;
  const timestamp = new Date().toISOString();
  const ref = `${timestamp}-${originalname}.webp`;
  await sharp(buffer)
    .webp({ quality: 20 })
    .toFile("./uploads/" + ref);
  const link = `http://localhost:3000/${ref}`;
  return res.json({ link });
});

app.listen(3000);
Enter fullscreen mode Exit fullscreen mode

Just use your favorite http client (in this case I used Insomnia) and don't forget to send the image with multipart/form-data and the name of the field must be “picture” and the type is file.

Like this:

screenshot

What about you?

What is your favorite image format?

Top comments (12)

Collapse
 
a14313 profile image
Carlo Autor

Thank you for this. This is what I have been looking for

Collapse
 
dilkashshaikhmahajan profile image
Dilkash Shaikh Shahagir Mahajan

After compress the image, I want to store the image in cloudinary then what will I do?

Collapse
 
franciscomendes10866 profile image
Francisco Mendes

I just happened to write an article about it, hope it helps! bit.ly/3w2UwHJ

Collapse
 
hnazmul profile image
H.Nazmul Hassan

wow so understandable docs. thanks you soo much

Collapse
 
franciscomendes10866 profile image
Francisco Mendes

Thank you for the feedback ☺

Collapse
 
yishai_zehavi profile image
Yishai Zehavi

That's a great article, thanks! My favorite image format would be AVIF, but it's still not supported as widely as WebP.

Collapse
 
solodovnykov profile image
Anton Solodovnykov

I have some problem, pls help to resolve this error
Image description

Collapse
 
kcouliba profile image
Coulibaly Daba Kevin

This is caused by the ":" character on windows OS.
Eventually you should replace
const timestamp = new Date().toISOString();
with
const timestamp = Date.now();

Collapse
 
sushmargowda27 profile image
Sushma R

Hi I used to this code and Compressed the image and storing in the MySQL database. but How to do decompress the image to retrieve the image from database.

Collapse
 
tarun18s profile image
tarun18s

i want to compress video how can i do that using sharp or any other way ??

Collapse
 
distil profile image
Charavner Louis

It's maybe too late for answer but you can use npmjs.com/package/fluent-ffmpeg for video compression

Collapse
 
jaypatel202002 profile image
JayPatel202002

after compression of image is done i want to upload it to my mongo DB database how can i achieve that?