- Published on
File Upload in Node.js Using Express.js and Multer
Still related to my previous post on CRUD REST API in Node.js Using Express.js and MongoDB, in this post, I want to create a tutorial on how to handle image file uploads using Node.js with the Express.js Web Framework and Multer.
What is Multer? Multer is a middleware for handling multipart/form-data, which is commonly used for file upload functionality.
So, what is middleware? It's an intermediary between the router and the handler / controller. In short, when we make a request to a specific route or endpoint, middleware will perform a certain process first before proceeding to the handler. It's commonly used for tasks like authentication, logging, input validation, and many other processes.
Preparation
First, here's a list of tools I used while creating this tutorial:
node.js v15.0.1
npm v7.0.3
yarn v1.22.5
Alright... let's start by creating a new directory and initializing the package.json for the project.
mkdir nodejs-file-upload
cd nodejs-file-upload
yarn init -y
Kemudian install paket yang dibutuhkan
yarn add express multer cors
Create the Main File
Create a new file named index.js in the root directory of the project.
// ./index.js
const express = require('express');
const cors = require('cors');
const port = process.env.PORT || 1337;
const routes = require('./routes');
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(routes);
app.listen(port, () => {
console.log('The magic happen on port :' + port);
});
Create Routes
Create a directory named routes in the root directory of the project and create a new file index.js inside it.
// routes/index.js
const express = require('express');
const router = express.Router();
const multer = require('multer');
const storage = multer.diskStorage({
// Set the directory where the uploaded images will be stored
destination: 'public/',
// Set the file name when the image is saved
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
const upload = multer({ storage: storage });
// Import the uploader handlernya
const handlerUploader = require('../handlers/uploader');
// Route that points to http://localhost:1337
router.get('/', async (req, res) => {
return res.json({ message: 'Hello World!' });
});
// Route that points to http://localhost:1337/uploader
router.post('/uploader', upload.single('image'), handlerUploader.single);
module.exports = router;
Create Helper
Create a directory named helpers in the root directory of the project and create a new file named uploader.js inside it
// helpers/uploader.js
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
// Function to generate a random string
exports.randomFilename = (length) => {
return crypto
.randomBytes(length)
.toString("hex");
}
// Function to validate the file extension of the uploaded file
exports.getValidExtension = (filename) => {
// List of file extensions
const formatAllowed = ["jpg", "jpeg", "png"];
// Get the extension of the uploaded file
const ext = filename
.substr(filename.lastIndexOf(".") + 1)
.toLowerCase();
// If the file extension matches one from the list
if (formatAllowed.indexOf(ext) > -1) {
// Return the string of the file extension
return ext;
}
else {
return false;
}
}
// Function to move the uploaded file
exports.saveImage = (pathFrom, pathTo) => {
try {
// Check if the target directory exists
if ( ! fs.existsSync(pathTo)) {
// If not, create the folder first
const destination = path.dirname(pathTo);
fs.mkdirSync(destination, { recursive: true });
}
// Rename the uploaded file with the new name and save it in the target directory
fs.renameSync(pathFrom, pathTo);
}
catch (error) {
throw (message = error);
}
};
Create Handler
Create a directory named handlers in the root directory of the project, and inside it, create a new file named uploader.js
// handlers/uploader.js
const fs = require('fs');
// Import the necessary functions from the uploader helper
const {
getValidExtension,
randomFilename,
saveImage
} = require('../helpers/uploader');
exports.single = async (req, res) => {
try {
// If no file is uploaded, return an error message
if ( ! req.file) {
return res.status(422).json({ message: `File tidak ditemukan.` });
}
// Check the file extension
const extension = await getValidExtension(req.file.originalname);
// If the extension is not valid
if ( ! extension) {
// Delete the uploaded file
fs.unlinkSync(req.file.path);
// Return an error response
return res.status(422).json({ message: `Format file tidak valid.` });
}
// Generate a new random file name
const filename = await randomFilename(16);
// Get the directory where the file is stored by multer
const pathFrom = req.file.path;
// Create the file storage directory if it doesn't exist
const pathTo = `public/images/${filename}.${extension}`;
// Save the image file
saveImage(pathFrom, pathTo);
const response = {
message: 'Upload berhasil.',
image_path: pathTo
}
return res.json(response);
}
catch (error) {
console.log(error);
return res.status(500).json(error);
}
}
Run the Server
Finally, try running the server using node, pm2, or nodemon:
$ node index.js
The magic happens on port :1337
If you see this message, the server is up and running.
Testing
I’ve created a collection for Postman to facilitate your testing. You can download the collection here.
You can check the project repository on GitHub.
That’s all for now, see you~