uploading and compressing image with multer and sharp in nodejs

Rishirishi
3 min readJan 20, 2021

lets start our simple project with package.json

npm init -y

and install dependencies and dev-dependencies that we’ll be using

npm i express multer sharp uuid chalknpm i -D nodemon

we will be looking into each of them shortly,

1. first, lets start our development server with express

const express = require('express');const chalk = require('chalk');const app = express();const PORT = 3000;app.listen(PORT, () => {console.log(chalk.bold.yellow('server is running on port ', PORT));});

this is simple express server running on port 3000. notice we used chalk in console.log(), it is only used to make our console logs in the terminal colorful and pretty.

2. now lets create a simple image upload route with multer without compression.

const UPLOAD_PATH = path.join(__dirname, '/uploads');const upload = multer(
{
storage,
limits,
fileFilter
}
).single('image');
app.post('/file-upload', (req, res) => {upload(req, res, async (err) => {if (err) {return res.status(400).json(
{ success: false, message: err.message }
);
}try {const { file } = req;if (!file) {return res.status(400).json(
{
success: false,
message: 'file not supplied'
}
);
}return res.status(200).json({ success: true, message: 'image
uploaded' });
} catch (error) {return res.status(500).json({ success: false, message:
error.message });
}});});

we have a route with multer middleware to upload an image. all the controller codes are wrapped inside

upload(req, res, async (err) => {});

for handling errors that may be thrown from multer middleware (upload variable). lets look into upload variable

const upload = multer(
{
storage,
limits,
fileFilter
}
).single('image');

.single(‘image’) says it is single image upload with image being uploaded as its key name “image” from front-end/postman. the other variables storage, limits, fileFilter are used for further processing and filtering of image.

const UPLOAD_PATH = path.join(__dirname, '/uploads');const storage = multer.diskStorage({destination: (req, file, done) => {done(null, UPLOAD_PATH);},filename: (req, file, done) => {done(null, uuid() + '___' + file.originalname);},});const limits = {fileSize: 5 * 1024 * 1024,};const fileFilter = (req, file, done) => {if (file.mimetype === 'image/jpeg' || file.mimetype === 
'image/png') {
done(null, true);} else {done(new Error('file type not supported'), false);}};

inside storage, there are two properties: destination and filename, the destination has folder location where images are to be stored. the filename would be the name of the image being stored. uuid is used to ensure that image names are unique so that they are not replaced when new image with same name is uploaded.

limits has a filesize property that specifies maximum file size allowed in bytes.

in fileFilter, we are filtering only jpg and png image types.

3. lets compress the image

what the following code does is, it saves a new compressed image and removes the original image. the path of new compressed image can be stored in db. lets update the ‘/file-upload’ route

app.post('/file-upload', (req, res) => {upload(req, res, async (err) => {if (err) {return res.status(400).json({ success: false, message: 
err.message });
}try {const { file } = req;if (!file) {return res.status(400).json({ success: false, message:
'file not supplied' });
}const newFilePath = path.join(UPLOAD_PATH, uuid() + '_' +
file.originalname);
// save newFilePath in your db as image pathawait sharp(file.path).resize().jpeg({ quality: 50
}).toFile(newFilePath);
fs.unlinkSync(file.path);return res.status(200).json({ success: true, message:
'image uploaded' });
} catch (error) {return res.status(500).json({ success: false, message:
error.message });
}});});

newFilePath generates a new filename with absolute path. but at first it has nothing. it is just a string. sharp then uses this path to store its newly compressed image. the fs.unlinkSync() removes original image from the file system.

4. mkdirp

all the uploaded images are stored in /uploads folder. sometimes multer fails to create this folder if it does not exist already. mkdirp solves this issue by creating /uploads folder if it does not exist already.

mkdirp.sync(path.join(__dirname, '/uploads'));

the good place to use this line of code is at the top before using multer.

5. full code link

Kritish58/multer-sharp (github.com)

thank you for reading, happy coding 😊

--

--