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 (6)

Collapse
hnazmul profile image
H.Nazmul Hassan

wow so understandable docs. thanks you soo much

Collapse
franciscomendes10866 profile image
Francisco Mendes Author

Thank you for the feedback ☺

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 Author

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

Collapse
solodovnykov profile image
Anton Solodovnykov

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

Collapse
tarun18s profile image
tarun18s

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

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.