Skip to content

Handling file uploads in forms using Express

FULL-STACK WEB DEVELOPMENT BOOTCAMP

2024 COHORT SIGNUPS END TOMORROW

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:

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)
    })
    //...
})
  • Learn modern web development in my BOOTCAMP (SIGNUP END TOMORROW FEB 20, 2024)
  • THE VALLEY OF CODE (+ PRO), your web development manual
  • I wrote 15+ coding BOOKS, all available in THE VALLEY OF CODE PRO
  • Indie solopreneur internet business masterclass SOLO LAB (summer 2024)