uploading and compressing image with multer and sharp in nodejs
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 😊