Upload An Image With Node.js
This short and concise guide shows how to set up image uploading with Node.js and Express.
If you’re in a hurry you can jump to the complete code snippet else let’s set it up step by step together.
Set Up The Server
We’ll start with a basic Express server.
Create a new folder and run npm init
to generate a package.json
file, then install Express.
npm install express
We’ll create an index.js
file in the root of our folder and we’ll copy the Express Hello world example to this file as a starting point.
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Let’s start the server.
node index.js
Open a browser and navigate to http://localhost:3000
, it should show “Hello World!”
Adding A Form
Let’s create a folder called public
and add a file called index.html
with the following HTML.
<!DOCTYPE html>
<form action="/upload" method="POST" enctype="multipart/form-data">
<input type="file" name="image" />
<button type="submit">Upload</button>
</form>
We’ve added the enctype
form attribute and set it to "multipart/form-data"
. This is needed to correctly post the file data contained in the file input field.
Note that we’ve set the name
attribute of our file input element to "image"
, this is the name we’ll use to read the image data on the server.
Our form will POST
to the post upload
route, which we’ll add soon. First we have to tell our server to serve the index.html
page.
const express = require('express');
const app = express();
const port = 3000;
// Add this line to serve our index.html page
app.use(express.static('public'));
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Next up we’ll add the upload
route.
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.post('/upload', (req, res) => {
// We'll handle the image upload here
});
To handle file uploads we need the express-fileupload
middleware, this will read the multipart/form-data and add a files
property to the request object.
Let’s install it now.
npm install express-fileupload
Now we’ll update the index.js
file to use express-fileupload
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
const port = 3000;
// Use the express-fileupload middleware
app.use(fileUpload());
app.use(express.static('public'));
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.post('/upload', (req, res) => {
// Log the files to the console
console.log(req.files);
// All good
res.sendStatus(200);
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Restart the server. When we refresh the page it now shows our form. When we upload a file, it logs the file to the server console.
It’ll look something like this
[Object: null prototype] {
image: {
name: 'test.jpeg',
data: <Buffer ff d8 ff e0 00 10 4a 46 49 46 00 01 01 01 00 48 00 48 00 00 ff e2 0c 58 49 43 43 5f 50 52 4f 46 49 4c 45 00 01 01 00 00 0c 48 4c 69 6e 6f 02 10 00 00 ... 1234567 more bytes>,
size: 234732,
encoding: '7bit',
tempFilePath: '',
truncated: false,
mimetype: 'image/jpeg',
md5: 'c76564e19a5e8cd647a2d60478ad94b3',
mv: [Function: mv]
}
}
The final step is to move the file to an upload folder.
First let’s create a folder called upload
in our project directory.
Then we’ll adjust the app.post
route to look like this.
app.post('/upload', (req, res) => {
// Get the file that was set to our field named "image"
const { image } = req.files;
// If no image submitted, exit
if (!image) return res.sendStatus(400);
// Move the uploaded image to our upload folder
image.mv(__dirname + '/upload/' + image.name);
res.sendStatus(200);
});
You can now find the uploaded file in the upload folder.
Securing The Upload
While this works it’s not super secure, our users can now upload anything to our server, let’s restrict them a bit so our server is more secure.
Only Allowing Images
Let’s first block all uploaded files that aren’t images.
app.post('/upload', (req, res) => {
const { image } = req.files;
if (!image) return res.sendStatus(400);
// If doesn't have image mime type prevent from uploading
if (!/^image/.test(image.mimetype)) return res.sendStatus(400);
image.mv(__dirname + '/upload/' + image.name);
res.sendStatus(200);
});
This would cover most file uploads, it’s possible malicious actors use a different fake a different mimetype so to be absolutely sure a file is of a certain type we have to inspect the file contents itself.
Limit Upload Size
We can tell express-fileupload
to limit the file upload size. This prevents users from uploading huge files in an attempt to DoS attack our server.
app.use(
fileUpload({
limits: {
fileSize: 10000000, // Around 10MB
},
abortOnLimit: true,
})
);
Now our server returns an HTTP 413
statuscode when the file is too big.
Preventing Malicious File Names
Malicious users can try to upload an image named ‘…/index.js’ but express-fileupload
will automatically strip those dots and the file will still end up in the correct folder.
The Final Node.js Image Upload Script
This the complete Node.js script ready for copy pasting.
const express = require('express');
const fileUpload = require('express-fileupload');
const app = express();
const port = 3000;
app.use(
fileUpload({
limits: {
fileSize: 10000000,
},
abortOnLimit: true,
})
);
// Add this line to serve our index.html page
app.use(express.static('public'));
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.post('/upload', (req, res) => {
// Get the file that was set to our field named "image"
const { image } = req.files;
// If no image submitted, exit
if (!image) return res.sendStatus(400);
// If doesn't have image mime type prevent from uploading
if (!/^image/.test(image.mimetype)) return res.sendStatus(400);
// Move the uploaded image to our upload folder
image.mv(__dirname + '/upload/' + image.name);
// All good
res.sendStatus(200);
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Conclusion
We’ve written a server script that can handle image uploads, we secured our code, and finally we’ve set up a form that allows users to select and upload images.
We could take this a step further and enable our users to edit the images before upload. This saves server bandwidth and improves the quality of the images uploaded, for example by always forcing a square image.
The <pintura-input>
element makes this straight forward. This Pintura powered web component automatically opens a powerful image editor when an image is added to the file input field and enables your users to edit images before upload.