Introduction
Users don't only consume data, they also produce data and upload it. They can send data through applications like messengers or email to specific recipients or upload files to social networks and data streaming platforms such as Facebook or YouTube.
That being said, almost every interactive website today supports file uploads.
File Uploading Libraries
There are several Node libraries available on NPM that can simplify the process of validating and uploading files to the server. Among them, the most popular choice these days are Multer, Formidable, and Multiparty.
They all have stable versions and are backed by an online community of open source developers.
What is Multer?
Multer is a popular Node.js middleware used for handling multipart/form-data
requests. It makes use of busboy to parse any data that it received through an HTML form. This greatly enhances its performance because the busboy module is unmatched when it comes to analyzing form data.
Multer provides us control and flexibility when handling multipart/form-data
requests - we get detailed information about each uploaded file, the ability to add a custom storage engine, validation of files according to our needs, the ability to set limits on uploaded files, etc.
Project Setup
Since we won't be storing our images in a database, but rather a simple folder for brevity and simplicity, let's make another folder within our project folder and name it, say, uploads
.
Now, let's install Express:
$ npm i express
And finally, let's install Multer:
$ npm i multer
Project Implementation
At this point, we are ready to write some code, starting off with the HTML forms that we'll use to harvest information.
Let's start off with the form for uploading a single file:
<form method="POST" action="/upload-profile-pic" enctype="multipart/form-data">
<div>
<label>Select your profile picture:</label>
<input type="file" name="profile_pic" />
</div>
<div>
<input type="submit" name="btn_upload_profile_pic" value="Upload" />
</div>
</form>
And then with a form that allows us to upload multiple files:
<form method="POST" action="/upload-multiple-images" enctype="multipart/form-data">
<div>
<label>Select multiple images:</label>
<input type="file" name="multiple_images" multiple />
</div>
<div>
<input type="submit" name="btn_upload_multiple_images" value="Upload" />
</div>
</form>
You can either put these forms on separate pages or on the same one. For the purpose of this tutorial, they're one after another:
The HTML forms are pretty straightforward, accepting multipart/form-data
and routing to the adequate functions that handle their requests.
Express Application
With our forms ready, we can work on the actual logic for uploading and validating files through Express.
Let's create a file called app.js
in the project root and start by importing the required modules:
const express = require('express');
const multer = require('multer');
const path = require('path');
Now, let's create our Express app:
const app = express();
And finally, let's set up the port on which it'll run:
Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!
const port = process.env.PORT || 3000;
The public
directory off of our root folder contains the static files we want to serve, so let's set it as a static directory using express.static
:
app.use(express.static(__dirname + '/public'));
At this point, let's define the storage location for our images:
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, 'uploads/');
},
// By default, multer removes file extensions so let's add them back
filename: function(req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
And finally, let's run the app using the port we set earlier:
app.listen(port, () => console.log(`Listening on port ${port}...`));
File Validation and Upload
For fundamental security reasons, we'll want to validate files before uploading them to our servers. Let's edit the app.js
file and add both functionalities:
app.post('/upload-profile-pic', (req, res) => {
// 'profile_pic' is the name of our file input field in the HTML form
let upload = multer({ storage: storage, fileFilter: helpers.imageFilter }).single('profile_pic');
upload(req, res, function(err) {
// req.file contains information of uploaded file
// req.body contains information of text fields, if there were any
if (req.fileValidationError) {
return res.send(req.fileValidationError);
}
else if (!req.file) {
return res.send('Please select an image to upload');
}
else if (err instanceof multer.MulterError) {
return res.send(err);
}
else if (err) {
return res.send(err);
}
// Display uploaded image for user validation
res.send(`You have uploaded this image: <hr/><img src="${req.file.path}" width="500"><hr /><a href="./">Upload another image</a>`);
});
});
Here, we've accepted an HTTP POST request, in which the image information is embodied. The function that actually takes care of the upload functionality is the multer().single()
method.
You might have noticed the fileFilter: helpers.imageFilter
but we haven't yet created/imported the helpers
file. So, let's create a new file in our project directory and name it helpers.js
. Here we will write some code that is used to check whether the submitted file is an image or not.
const imageFilter = function(req, file, cb) {
// Accept images only
if (!file.originalname.match(/\.(jpg|JPG|jpeg|JPEG|png|PNG|gif|GIF)$/)) {
req.fileValidationError = 'Only image files are allowed!';
return cb(new Error('Only image files are allowed!'), false);
}
cb(null, true);
};
exports.imageFilter = imageFilter;
Of course, to use this module, we'll have to import it at the top of our app.js
file:
const helpers = require('./helpers');
Now, we can run our application and validate that it's working properly:
Uploading Multiple Files
Uploading multiple files is essentially the same as uploading a single file. Though, instead of the multer().single()
function, we use the multer().array()
function:
app.post('/upload-multiple-images', (req, res) => {
// 10 is the limit I've defined for number of uploaded files at once
// 'multiple_images' is the name of our file input field
let upload = multer({ storage: storage, fileFilter: helpers.imageFilter }).array('multiple_images', 10);
upload(req, res, function(err) {
if (req.fileValidationError) {
return res.send(req.fileValidationError);
}
else if (...) // The same as when uploading single images
let result = "You have uploaded these images: <hr />";
const files = req.files;
let index, len;
// Loop through all the uploaded images and display them on frontend
for (index = 0, len = files.length; index < len; ++index) {
result += `<img src="${files[index].path}" width="300" style="margin-right: 20px;">`;
}
result += '<hr/><a href="./">Upload more images</a>';
res.send(result);
});
});
And now, to validate if everything is working correctly:
Conclusion
Users don't only consume data, they produce data and, in many cases, need to upload it to a web server. They can send data through applications like messengers or email to specific recipients, or they can upload files to social networks and data streaming platforms such as Facebook or YouTube.
In this article we've used Express.js and the Multer library to handle basic file uploading functionality in a simple web application.