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
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);
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);
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.
});
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.
});
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.
});
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...
});
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);
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:
What about you?
What is your favorite image format?
Top comments (13)
Thank you for this. This is what I have been looking for
After compress the image, I want to store the image in cloudinary then what will I do?
I just happened to write an article about it, hope it helps! bit.ly/3w2UwHJ
Hello, I want delete the origin image, but i can't, why ?
wow so understandable docs. thanks you soo much
Thank you for the feedback ☺
That's a great article, thanks! My favorite image format would be AVIF, but it's still not supported as widely as WebP.
I have some problem, pls help to resolve this error
This is caused by the ":" character on windows OS.
Eventually you should replace
const timestamp = new Date().toISOString();
with
const timestamp = Date.now();
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.
i want to compress video how can i do that using sharp or any other way ??
It's maybe too late for answer but you can use npmjs.com/package/fluent-ffmpeg for video compression
after compression of image is done i want to upload it to my mongo DB database how can i achieve that?