Handling file uploads in forms using Express
New Courses Coming Soon
Join the waiting lists
How to manage storing and handling files uploaded via forms, in Express
This is an example of an HTML form that allows a user to upload a file:
<form method="POST" action="/submit-form" enctype="multipart/form-data">
<input type="file" name="document" />
<input type="submit" />
</form>
Don’t forget to add
enctype="multipart/form-data"
to the form, or files won’t be uploaded
When the user press the submit button, the browser will automatically make a POST
request to the /submit-form
URL on the same origin of the page. The browser sends the data contained, not encoded as as a normal form application/x-www-form-urlencoded
, but as multipart/form-data
.
Server-side, handling multipart data can be tricky and error prone, so we are going to use a utility library called formidable. Here’s the GitHub repo, it has over 4000 stars and is well-maintained.
You can install it using:
npm install formidable
Then include it in your Node.js file:
const express = require('express')
const app = express()
const formidable = require('formidable')
Now, in the POST
endpoint on the /submit-form
route, we instantiate a new Formidable form using formidable.IncomingForm()
:
app.post('/submit-form', (req, res) => {
new formidable.IncomingForm()
})
After doing so, we need to be able to parse the form. We can do so synchronously by providing a callback, which means all files are processed, and once formidable is done, it makes them 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 file of Object.entries(files)) {
console.log(file)
}
})
})
Or, you can use events instead of a callback. For example, to be notified when each file is parsed, or other events such as completion of file processing, receiving a non-file field, or if an error occurred:
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()
})
})
Whichever way you choose, you’ll get one or more Formidable.File objects, which give you information about the file uploaded. These are some of the methods you can call:
file.size
, the file size in bytesfile.path
, the path the file is written tofile.name
, the name of the filefile.type
, the MIME type of the file
The path defaults to the temporary folder and can be modified if you listen 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)
})
//...
})
Here is how can I help you:
- COURSES where I teach everything I know
- CODING BOOTCAMP cohort course - next edition in 2025
- BOOKS 16 coding ebooks you can download for free on JS Python C PHP and lots more
- Follow me on X