Learn how to handle and store files uploaded via forms in Express.
This is an example of an HTML form that allows users to upload files:
<form method="POST" action="/submit-form" enctype="multipart/form-data">
<input type="file" name="document" />
<input type="submit" />
</form>
Don’t forget to include
enctype="multipart/form-data"
in the form, otherwise files won’t be uploaded.
When the user presses the submit button, the browser will send a POST
request to the /submit-form
URL on the same origin of the page. The data is sent as multipart/form-data
instead of the normal form application/x-www-form-urlencoded
encoding.
Handling multipart data on the server-side can be challenging and error-prone. To simplify this process, we will use a utility library called formidable. You can find the GitHub repository here, which has over 4000 stars and is well-maintained.
To install formidable, run the following command:
npm install formidable
Next, include the formidable module in your Node.js file:
const express = require('express');
const app = express();
const formidable = require('formidable');
Now, in the POST
endpoint for the /submit-form
route, create a new instance of Formidable using formidable.IncomingForm()
:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm();
});
After instantiating the Formidable form, we need to be able to parse the form data. We can do this synchronously by providing a callback function. This means that all files are processed, and once formidable has finished, they become available:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm().parse(req, (err, fields, files) => {
if (err) {
console.error('Error', err);
throw err;
}
console.log('Fields', fields);
console.log('Files', files);
for (const [name, file] of Object.entries(files)) {
console.log(name, file);
}
});
});
Alternatively, you can use events instead of a callback. For example, to be notified when each file is parsed or for other events such as file processing completion, receiving a non-file field, or encountering an error:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm().parse(req)
.on('field', (name, field) => {
console.log('Field', name, field);
})
.on('file', (name, file) => {
console.log('Uploaded file', name, file);
})
.on('aborted', () => {
console.error('Request aborted by the user');
})
.on('error', (err) => {
console.error('Error', err);
throw err;
})
.on('end', () => {
res.end();
});
});
Regardless of which approach you choose, you’ll receive one or more Formidable.File objects, which provide information about the uploaded file. Here are some of the methods you can use:
file.size
: the file size in bytesfile.path
: the path where the file is written tofile.name
: the name of the filefile.type
: the MIME type of the file
By default, the file is stored in the temporary folder. You can modify the file path by listening for the fileBegin
event:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm().parse(req)
.on('fileBegin', (name, file) => {
file.path = __dirname + '/uploads/' + file.name;
})
.on('file', (name, file) => {
console.log('Uploaded file', name, file);
})
// ...
});