Join the AI Workshop and learn to build real-world apps with AI. A hands-on, practical program to level up your skills.
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 presses the submit button, the browser will send a POST request to the /submit-form URL on the same origin. The data is sent not as 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 by passing a callback: when Formidable has finished parsing, it will call the callback with the parsed fields and files:
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 with information about the uploaded file. Here are some of the properties you can use:
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)
})
//...
})