In this tutorial, I will show you how you can handle the uploaded files using multer library in your express server. We'll also see how you can resize the uploaded images and save them in JPEG format. So let's get started by creating a new node project.
Open your terminal/cmd and start typing these commands.
mkdir file-upload
cd file-upload
npm i express multer
touch app.js #on windows: type nul > app.js
Let's import all the dependencies, create a multer object and start a basic express server at port 5000.
const express = require('express');
const multer = require('multer');
const app = express();
const upload = multer({dest: 'public/uploads'})
app.use(express.static(__dirname + '/public'));
app.listen(5000, () => console.log(`Listening on port 5000`));
Let's create a route that lets us upload a single file.
To upload the single file you need to call the single method of the multer object.
app.post('/upload-pic', function (req, res) {
upload.single('file')(req, res, function(err){
if (req.fileValidationError) return res.status(400).json({msg: req.fileValidationError})
else if (err) return res.status(400).json({msg: err.code})
else if (!req.file) return res.status(400).json({msg: "Please upload the file"})
return res.json({file: req.file})
})
})
As you can see when we call the single method it returns the middleware function. The third argument of middleware is the function that takes an error as an argument and here we use it to handle multiple types of errors that may occur.
Now let's test the code by uploading a file.
http -f POST http://localhost:5000/upload-pic file@C:\Users\shiva\Desktop\test.jpg
HTTP/1.1 200 OK
...
{
"file": {
"destination": "public/uploads/",
"encoding": "7bit",
"fieldname": "file",
"filename": "1641031086782.jpg",
"mimetype": "image/jpeg",
"originalname": "test.jpg",
"path": "public\\uploads\\1641031086782.jpg",
"size": 54509
}
}
It worked!
To upload multiple files you need to call the array method of the multer object. It also expects an optional max file argument. In this example, I set it to 2.
app.post('/upload-pic/multiple', function (req, res) {
upload.array('file', 2)(req, res, function(err){
if (req.fileValidationError) return res.status(400).json({msg: req.fileValidationError})
else if (err) return res.status(400).json({msg: err.code})
else if (!req.files) return res.status(400).json({msg: "Please upload the files"})
return res.json({file: req.files})
})
})
If you see the saved files don't have any file extension in them. So to save the files with their original extension you need to create a disk storage object and specify the filename function inside it.
...
const path = require("path");
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + path.extname(file.originalname))
}
})
const upload = multer({ storage })
...
Don't forget to pass the storage into multer object.
The biggest issue with our code is that users can upload any type of file and with any size. So let's make a filter function that only allows the users to upload image files with a maximum size of 100KB.
const fileFilter = function (req, file, callback) {
const isPhoto = file.mimetype.indexOf("image/") === 0
if (!isPhoto) {
req.fileValidationError = 'Only image files are allowed';
callback(new Error("Only image files are allowed"), false)
}else{
callback(null, true)
}
}
const limits = {fileSize: 100000} // 100 kb
const upload = multer({storage, fileFilter, limits})
Don't forget to pass the filter function and size limit to the multer function.
To save disk space and reduce the server cost we can resize the images to the desired size and save them in JPEG format.
All this can be easily done with a library called sharp. So let's install it.
npm i sharp
The approach we are going to use is first we save the input image then load it to sharp, resize it and save it as a JPEG image. Finally, delete the previously saved image.
const sharp = require("sharp")
const fs = require("fs")
app.post('/upload-pic', function (req, res) {
upload.single('file')(req, res, async function(err){
if (req.fileValidationError) return res.status(400).json({msg: req.fileValidationError})
else if (err) return res.status(400).json({msg: err.code})
else if (!req.file) return res.status(400).json({msg: "Please upload the file"})
await sharp(req.file.path)
.resize(400, 400)
.jpeg({ quality: 90 })
.toFile(path.resolve(req.file.destination,'resized' + req.file.filename))
fs.unlinkSync(req.file.path)
return res.json({msg: "file uploaded successfully"})
})
})