How to Upload Images with Express
Learn how to upload images to an Express server using the Multer middleware. We will also learn how to handle HTML forms with Express, different types of HTML form data encodings, and how to serve up static content with Express.
Table of Contents 📖
Project Demonstration
Below is what we are going to be building. We will upload images to an Express server and then display every uploaded image.
Project Setup
To start, lets initialize an empty directory as an npm ES6 project.
npm init es6 -y
Now lets install the required dependencies.
npm i express multer
- express - A web application framework for Node.
- multer - An Express middleware for handling multipart/form-data. Primarily used for uploading files.
We will use the multer middleware to handle image uploads from an HTML form. Now lets install nodemon as a development dependency.
npm i nodemon -D
- nodemon - Automatically restarts a Node application when files change.
Now lets define our environment variables. These include the port and host of our Express server.
NODE_ENV=development
HOST=localhost
PORT=1234
Now lets create a start script to run our application with nodemon and provide our .env file.
"main": "src/server.js",
...
"scripts": {
"start": "nodemon --env-file .env ."
}
INFO: Note that to use the --env-file flag we must be using Node version 20.11.0 or higher.
Express Middleware Setup
Now lets start building our Express server. First we will import our libraries and create some variables.
import express from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
const HOST = process.env.HOST;
const PORT = process.env.PORT;
Now that we've defined our variables, lets create our HTML form that will be served up by our Express server.
const FILE_NAME = 'FILE_UPLOADED';
const HTML_FORM_TEMPLATE = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Uploader</title>
</head>
<body>
<a href="/images">View Uploaded Images</a>
<h1>Upload an image to Express</h1>
<form method='post' action='/images' enctype="multipart/form-data">
<input type='file' name='${FILE_NAME}'>
<input type='submit'>
</form>
</body>
</html>
`;
Here we create an anchor tag to take us to a page that will display all the images we have uploaded, create an H1 tag to display the text "Upload an image to Express", and create a form with some inputs that will allow us to upload an image. The attributes of the form and input tags are important
- method - The HTTP method to use. In this case it is POST.
- action - The URL to send the form data to.
- enctype - Specifies how the form data should be encoded when submitted to the server. The multipart/form-data encoding is the preferred media type for files, non-ASCII, and binary data.
- type - The type of input. Setting type to file will allow us to upload files. Setting type to submit will submit the form data.
- name - Used to reference the form data element. This is important for working with the data with multer and Express.
Now lets configure Express to serve up static content from a directory called public.
const app = express();
app.use(express.static(path.resolve(import.meta.dirname, 'public')));
INFO: Note that to use import.meta.dirname we must be using Node version 20.11.0 or higher.
We also want to tell multer to save our files to this directory.
const upload = multer({dest: path.resolve(import.meta.dirname, 'public')});
Calling multer returns an Express middleware. This function accepts a configuration object where one of these options is dest. This is where to store the processed files. Now lets add some logging middleware and send our HTML form.
// Logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
return next();
});
// Serve up HTML form
app.get('/', (req, res) => {
return res.send(HTML_FORM_TEMPLATE);
});
Now we need to handle the image upload route that our HTML form will submit to.
// Handle image upload
app.post('/images', upload.single(FILE_NAME), (req, res) => {
/**
{
fieldname: 'FILE_UPLOADED',
originalname: 'jungle-symbol.png',
encoding: '7bit',
mimetype: 'image/png',
destination: '/Users/wittcode/Documents/IN_PROGRESS/googleapis-login/src/uploads',
filename: 'dcd071c0c6895d1815b82eb6786d8cd8',
path: '/Users/wittcode/Documents/IN_PROGRESS/googleapis-login/src/uploads/dcd071c0c6895d1815b82eb6786d8cd8',
size: 3721
}
*/
console.log(req.file);
return res.redirect('/images');
});
Here we upload the file using the multer middleware and then redirect the user to the /images route. The single method accepts a single file with the name provided. The provided name must be the same as the one provided to the input element name attribute. This middleware also appends the uploaded file to the request object under the file property. Now lets create the images GET route.
// Display the uploaded images
app.get('/images', async (req, res) => {
const images = await fs.promises.readdir(path.resolve(import.meta.dirname, 'public'));
const HTML_IMAGES = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<a href="/">Home</a>
<h1>Uploaded Images</h1>
${images.map(i => `<img height="300px" width="200px" src="${i}">`).join('')}
</body>
</html>`;
return res.send(HTML_IMAGES);
});
- Get all the iamges from the public directory. Where Multer is placing the images.
- Create an HTML page that displays the images.
Finally we just need to start our server on the provided port and host.
app.listen(PORT, HOST, () => {
console.log(`Server running on ${HOST}:${PORT}`);
});
Starting the Server
To run the application, navigate to where package.json is and run npm start.
npm start
> start
> nodemon --env-file .env .
[nodemon] 3.1.0
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,cjs,json
[nodemon] starting `node --env-file .env .`
Server running on localhost:1234