How to handle file upload in Expressjs

Step 1: Initiate the project

mkdir uploader && cd uploader
npm init -y

The first line creates an empty directory and moves into it. A new NodeJS project is initiated in this directory.

A package.json file is created with the default details.

The package.json file should look just like this:

{
  "name": "uploader",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "directories": {
    "test": "test"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

This configuration takes the name from the folder name. It also assumes that we have an index.js file as our main file.

Step 2: Create an ExpressJS server

Create a new index.js file and put the following code in it:

// Import dependencies
const express = require('express')
const app = express()
var bodyParser = require('body-parser')

// Set port
const port = 3000

// Register middlewares
app.use(bodyParser.urlencoded({ extended: false }))

// Home route
app.get('/', (req, res) => {
  res.send('Hello World!')
})

// Start listening
app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`)
})

This file is our main application file. It requires a few dependencies that can be installed using:

npm i body-parser express
npm i -D nodemon

The first dependency, body-parser, is a middleware that adds the request data to the request. The dev dependency nodemon restarts the server whenever a change is made to the code, which ensures that the server is always running.

Now you have a new ExpressJS server ready to go. Run the server using nodemon.

nodemon index.js

Now the server is active and can listen for requests.

You can test by visiting http://localhost:3000 on your browser.

Step 3: Create a form view

Next, to handle file upload, a file has to be uploaded. To upload a file, an HTML form can be used.

Create an index.html file in the directory, at the same level as our main file, index.js, and add in the following content:

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<title>Hello, world!</title>
</head>
<body class="container">
<form class="card m-5 mx-auto p-5" action="/upload" method="POST" enctype="multipart/form-data">
<!-- Username -->
<div class="mb-3">
<label for="username" class="form-label">Your Name</label>
<input type="text" class="form-control" name="username">
</div>
<!-- Image File -->
<div class="mb-3">
<label for="image" class="form-label">Image</label>
<input type="file" class="form-control" name="image">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</body>
</html>

This file uses the starter template from the Bootstrap website. The page has a text input and a file input to upload the target file. Update the index.js file to render this view:

app.get('/', (req, res) => {
  // Render form view
  res.sendFile(__dirname + '/index.html')
})

This route now renders the newly created HTML file, accessing it using its relative path.

Visiting the page again returns a view like this:

widget

Notice how you do not have to manually restart the server; nodemon restarts it when you save new changes. So, as long as the server was started as stated in Step 1, it should still be running.

Step 4: Handle the uploaded file

Next, when the form is submitted, we want to receive the file. This involves a few steps.

First, create an endpoint to receive the form request.

// Image processing route
app.post('/upload', (req, res) => {
  res.send('Submitted')
})

At this point, the form only prints the text 'Submitted' when submitted. Test this by actually submitting the form.

In earlier versions of ExpressJS, files can be accessed easily as part of the request object using req.files. In recent versions, this property is no longer available on the request object. Now, you can use multer, a separate package, to access uploaded files.

Install multer:

npm i multer

Instantiate multer in the index.js file:

// Import multer like the other dependencies
const multer  = require('multer')

// Set multer file storage folder
const upload = multer({ dest: 'uploads/' })

Here, we import multer and instantiate it by telling it where to store all uploaded files.

To use multer, add the middleware to the endpoint:

app.post('/upload', upload.single('image'), (req, res) => {
  console.log(req.file)
})

The middleware adds a file property to the request object. This property will only have some value if the name of the file input in the form matches the name it expects.

In this case, it expects the file name to be image, as shown in upload.single('image'). The middleware also stores the uploaded file in the stated uploads folder.

Testing this current logs the uploaded file, and it has the following structure:

{
  fieldname: 'image',
  originalname: 'Screenshot 2021-11-02 at 00.12.28.png',
  encoding: '7bit',
  mimetype: 'image/png',
  destination: 'uploads/',
  filename: 'a15af77a31eb44b4a6b043db90f15214',
  path: 'uploads/a15af77a31eb44b4a6b043db90f15214',
  size: 538524
}

At this point, you now have access to the file using req.file and all these properties by adding the property name to req.file using object notation. For example, to see the file path, use req.file.path.