Bootcamp
Search…
3.4.11: File Uploads

Introduction

In 3.1.3: POST Requests, we added an express.urlencoded middleware to populate request.body. For file uploads, we will add a similar configuration to Express using the Multer library to accept binary file data in requests.

Multer Setup in Express

Multer names and saves uploaded files to a local folder on the server, and adds file metadata to the Express request object in a file key.

Install multer

npm install multer

Create File Upload Directory

Create an uploads directory in our repo to store files.
mkdir uploads

Add uploads to .gitignore

We want Git to save the existence ****of the uploads folder, but ignore the files in it because the files in uploads are not part of application logic. Ignoring the files in uploads also prevents our Git repo from getting unnecessarily large.
.gitignore
uploads/*

Initialise `multer` in index.js

Specify the uploads destination folder when initialising multer. See Multer docs for details.

index.js

import multer from 'multer';
​
// set the name of the upload directory here
const multerUpload = multer({ dest: 'uploads/' });

Update Application Logic to Accept File Uploads

Update HTML Form to Accept File Data

  1. 1.
    Add enctype="multipart/form-data" attribute to our opening form tag. This signals that the form contains file data.
  2. 2.
    Add an input tag of type file to accept file data in the form.

upload.ejs

<form action="/recipe" method="post" enctype="multipart/form-data">
<label for="label">recipe label:</label><br />
<input type="text" id="label" name="label" /><br />
<label for="photo">recipe photo:</label><br />
<input type="file" name="photo" />
<input type="submit" value="Submit" />
</form>

Add Multer Middleware to POST Route

Add multerUpload.single middleware to the POST route where we plan to receive the file upload. Provide the file upload form input field's name attribute as a parameter to the middleware.

index.js

app.get('/recipe', (request, response) => {
response.render('upload');
});
​
app.post('/recipe', multerUpload.single('photo'), (request, response) => {
console.log(request.file);
response.send('worked');
});

Use File Metadata in request.file

In production apps we would take the information from request.file and save it to the DB. Notice how the file name is a cryptographic hash, and there is no file extension. This is deliberate to ensure file names are unique. If we wish to name files differently Multer accepts configuration options at initialisation.

request.file

{
fieldname: 'photo',
originalname: 'hickey.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: 'b6c6612935244b080fc2daa81ac1b3b8',
path: 'uploads/b6c6612935244b080fc2daa81ac1b3b8',
size: 422224
}

Save File Upload Metadata to DB

In this example we save uploaded file's name to our DB so our app can retrieve the file when it needs to. We save the file name to a column called photo in the recipes table.

Create Column to Store File Name

In this example our file name column is photo. Please use a more descriptive name depending on your context.

init_tables.sql

CREATE TABLE recipes (
id INTEGER NOT NULL PRIMARY KEY,
label TEXT,
photo TEXT
);

Store File Name in DB from Route Middleware

When processing the request in our route middleware, store the uploaded file's name in a new row in the DB.

index.js

app.post('/recipe', multerUpload.single('photo'), (request, response) => {
console.log('request came in');
console.log(request.file);
​
const sqlQuery = 'INSERT INTO recipes (label, photo) VALUES ($1, $2)';
// get the photo column value from request.file
const values = [request.body.label, request.file.filename];
​
// Query using pg.Pool instead of pg.Client
pool.query(sqlQuery, values, (error, result) => {
if (error) {
console.log('Error executing query', error.stack);
response.status(503).send(error.message);
return;
}
console.log(result.rows[0].label);
response.send(result.rows);
});
});

Retrieve Uploaded Files in the App

To retrieve uploaded files, we will reference the uploaded file's location and use it to render the file. In this example because the uploaded file is an image, we will use the file's location to create an img tag in our EJS template. When the rendered HTML page loads client-side, it will make a request for the file via the img tag, and Express will serve the file.

Configure Express to Retrieve Static Files from uploads

Add express.static config to allow Express to serve files from the uploads folder. Express supports multiple express.static configs so we don't need to worry about conflicts with our other static file folders for files such as CSS.
app.use(express.static('uploads'));

Test File Retrieval

To test this, try accessing the file directly from our server.

Request Template

Note that <UPLOADED_FILENAME> references the Multer-generated filename, not the filename on our local computers.
http://localhost:3000/<UPLOADED_FILENAME>

Sample Request

The file name in this example is from the request.file example above.
http://localhost:3000/b6c6612935244b080fc2daa81ac1b3b8
This should cause Express to send the file back. We should see it as a download.

Retrieve File Name using SQL in Express

This GET route's logic in index.js doesn't need to change. Our SQL query now returns the uploaded file's name because we stored it in the photo column in the recipes table.

index.js

app.get('/recipe/:id', (request, response) => {
const sqlQuery = 'SELECT * FROM recipes WHERE id=$1;';
const values = [request.params.id];
​
// Query using pg.Pool instead of pg.Client
pool.query(sqlQuery, values, (error, result) => {
if (error) {
console.log('Error executing query', error.stack);
response.status(503).send(error.message);
return;
}
console.log(result.rows[0].label);
const data = {
recipe: result.rows[0],
};
response.render('recipe', data);
});
});

Render File using File Name in EJS

Using the data retrieved from our DB, we render the recipe photo in our EJS template. We use the photo attribute of the recipe record to request the photo file in the uploads folder. photo contains the file name stored in the database.

recipe.ejs

<h3>Recipe Photo:</h3>
<p>
<img src="/<%= recipe.photo %>" />
</p>

Looking Ahead

In production applications we would rarely store file uploads on the same machine as our web application server (Express in our case). This is because our app servers have limited storage space, and storing file uploads for many users would quickly exhaust this space. In practice, file uploads are typically stored with a 3rd-party file hosting service such as AWS S3. We will learn this in a later module, and for now it is ok to store uploaded files locally on the app server.
Copy link
On this page
Introduction
Multer Setup in Express
Install multer
Create File Upload Directory
Add uploads to .gitignore
Initialise `multer` in index.js
Update Application Logic to Accept File Uploads
Update HTML Form to Accept File Data
Add Multer Middleware to POST Route
Use File Metadata in request.file
Save File Upload Metadata to DB
Create Column to Store File Name
Store File Name in DB from Route Middleware
Retrieve Uploaded Files in the App
Configure Express to Retrieve Static Files from uploads
Test File Retrieval
Retrieve File Name using SQL in Express
Render File using File Name in EJS
Looking Ahead