In this tutorial, I will show you how to upload file with Node.js Express Rest APIs to/from a static folder using Multer (with file size limit).
This Node.js App works with:
– Angular 8 / Angular 10 / Angular 11 / Angular 12 / Angular 13 / Angular 14
– Angular Material 12
– Vue Client / Vuetify Client
– React Client / React Hooks Client
– React Image Upload with Preview
– Material UI Client
– Axios Client
Related Posts:
– Node.js delete File Rest API example
– Google Cloud Storage with Node.js: File Upload example
– How to upload multiple files in Node.js
– Upload/store images in MySQL using Node.js, Express & Multer
– How to upload/store images in MongoDB using Node.js, Express & Multer
– Node.js Rest APIs example with Express, Sequelize & MySQL
Contents
- Node.js Express Rest APIs for uploading Files
- Technology
- Project Structure
- Setup Node.js Express File Upload project
- Create middleware for file upload
- Restrict file size with Multer
- Create Controller for file upload/download
- Handle Multer file size limit error
- Define Route for uploading file
- Create Express app server
- Run & Test
- Conclusion
- Further Reading
- Source Code
Node.js Express Rest APIs for uploading Files
Our Node.js Application will provide APIs for:
- uploading File to a static folder in the Server (restrict file size: 2MB)
- downloading File from server with the link
- getting list of Files’ information (file name & url)
- deleting File from server by file name
This is the static folder that stores all uploaded files:
If we get list of files, the Node.js Rest Apis will return:
Each item in the response array has a url that you can use for downloading the file.
These are APIs to be exported:
Methods | Urls | Actions |
---|---|---|
POST | /upload | upload a File |
GET | /files | get List of Files (name & url) |
GET | /files/[filename] | download a File |
DELETE | /files/[filename] | delete a File |
For more details about deleting File, please visit:
Node.js delete File Rest API example
Technology
- express 4.18.1
- multer 1.4.4
- cors 2.8.5
Project Structure
This is the project directory that we’re gonna build:
– resources/static/assets/uploads
: folder for storing uploaded files.
– middleware/upload.js
: initializes Multer Storage engine and defines middleware function to save uploaded files in uploads
folder.
– file.controller.js
exports Rest APIs: POST a file, GET all files’ information, download a File with url.
– routes/index.js
: defines routes for endpoints that is called from HTTP Client, use controller to handle requests.
– server.js
: initializes routes, runs Express app.
Setup Node.js Express File Upload project
Open command prompt, change current directory to the root folder of our project.
Install Express, Multer, CORS modules with the following command:
npm install express multer cors
The package.json file will look like this:
{
"name": "node-js-express-upload-download-files",
"version": "1.0.0",
"description": "Node.js Express Rest APis to Upload/Download Files",
"main": "server.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node js",
"upload",
"download",
"file",
"multipart",
"rest api",
"express"
],
"author": "bezkoder",
"license": "ISC",
"dependencies": {
"cors": "^2.8.5",
"express": "^4.17.1",
"multer": "^1.4.2"
}
}
Create middleware for file upload
The middleware will use Multer for handling multipart/form-data along with uploading files.
Inside middleware folder, create upload.js file:
const util = require("util");
const multer = require("multer");
const maxSize = 2 * 1024 * 1024;
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, __basedir + "/resources/static/assets/uploads/");
},
filename: (req, file, cb) => {
console.log(file.originalname);
cb(null, file.originalname);
},
});
let uploadFile = multer({
storage: storage,
limits: { fileSize: maxSize },
}).single("file");
let uploadFileMiddleware = util.promisify(uploadFile);
module.exports = uploadFileMiddleware;
In the code above, we’ve done these steps:
– First, we import multer
module.
– Next, we configure multer
to use Disk Storage engine.
You can see that we have two options here:
– destination
determines folder to store the uploaded files.
– filename
determines the name of the file inside the destination
folder.
util.promisify()
makes the exported middleware object can be used with async-await
.
Restrict file size with Multer
With the new multer
API. We can add limits: { fileSize: maxSize }
to the object passed to multer()
to restrict file size.
let storage = multer.diskStorage(...);
const maxSize = 2 * 1024 * 1024;
let uploadFile = multer({
storage: storage,
limits: { fileSize: maxSize }
}).single("file");
Create Controller for file upload/download
In controller folder, create file.controller.js:
– For File Upload method, we will export upload()
function that:
- use middleware function for file upload
- catch Multer error (in middleware function)
- return response with message
– For File Information and Download:
getListFiles()
: read all files in uploads folder, return list of files’ information (name, url)download()
: receives file name as input parameter, then uses Express res.download API to transfer the file at path (directory + file name) as an ‘attachment’.
const uploadFile = require("../middleware/upload");
const upload = async (req, res) => {
try {
await uploadFile(req, res);
if (req.file == undefined) {
return res.status(400).send({ message: "Please upload a file!" });
}
res.status(200).send({
message: "Uploaded the file successfully: " + req.file.originalname,
});
} catch (err) {
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
};
const getListFiles = (req, res) => {
const directoryPath = __basedir + "/resources/static/assets/uploads/";
fs.readdir(directoryPath, function (err, files) {
if (err) {
res.status(500).send({
message: "Unable to scan files!",
});
}
let fileInfos = [];
files.forEach((file) => {
fileInfos.push({
name: file,
url: baseUrl + file,
});
});
res.status(200).send(fileInfos);
});
};
const download = (req, res) => {
const fileName = req.params.name;
const directoryPath = __basedir + "/resources/static/assets/uploads/";
res.download(directoryPath + fileName, fileName, (err) => {
if (err) {
res.status(500).send({
message: "Could not download the file. " + err,
});
}
});
};
module.exports = {
upload,
getListFiles,
download,
};
– We call middleware function uploadFile()
first.
– If the HTTP request doesn’t include a file, send 400
status in the response.
– We also catch the error and send 500
status with error message.
So, how to handle the case in that user uploads the file exceeding size limitation?
Handle Multer file size limit error
We can handle the error by checking error code (LIMIT_FILE_SIZE
) in the catch()
block:
const upload = async (req, res) => {
try {
await uploadFile(req, res);
...
} catch (err) {
if (err.code == "LIMIT_FILE_SIZE") {
return res.status(500).send({
message: "File size cannot be larger than 2MB!",
});
}
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
};
Define Route for uploading file
When a client sends HTTP requests, we need to determine how the server will response by setting up the routes.
There are 3 routes with corresponding controller methods:
- POST
/upload
:upload()
- GET
/files
:getListFiles()
- GET
/files/[fileName]
:download()
Create index.js file inside routes folder with content like this:
const express = require("express");
const router = express.Router();
const controller = require("../controller/file.controller");
let routes = (app) => {
router.post("/upload", controller.upload);
router.get("/files", controller.getListFiles);
router.get("/files/:name", controller.download);
app.use(router);
};
module.exports = routes;
You can see that we use controller from file.controller.js.
Create Express app server
Finally, we create an Express server in server.js:
const cors = require("cors");
const express = require("express");
const app = express();
global.__basedir = __dirname;
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
const initRoutes = require("./src/routes");
app.use(express.urlencoded({ extended: true }));
initRoutes(app);
let port = 8080;
app.listen(port, () => {
console.log(`Running at localhost:${port}`);
});
What we do are:
– import express
and cors
modules:
- Express is for building the Rest apis
- cors provides Express middleware to enable CORS with various options.
– create an Express app, then add cors
middlewares using app.use()
method. Notice that we set origin: http://localhost:8081
.
– listen on port 8080 for incoming requests.
Run & Test
First we need to create uploads folder with the path resources/static/assets
.
On the project root folder, run this command: node server.js
.
Let’s use Postman to make HTTP POST request with a file.
– Upload a file with size larger than max file size (2MB):
– Check uploads folder after uploading several files:
– Retrieve list of Files’ information:
– Now you can download any file from one of the paths above.
For example: http://localhost:8080/files/bezkoder.png
.
Conclusion
Today we’ve learned how to create Node.js Express Rest API to upload file into static folder using Multer for middleware handling multipart file. You also know how to restrict file size and catch Multer file size limit error.
For more details about deleting File, please visit:
Node.js delete File Rest API example
Following tutorials explain how to build Front-end Apps to work with our Node.js Express Server:
– Angular 8 / Angular 10 / Angular 11 / Angular 12 / Angular 13 / Angular 14
– Angular Material 12
– Vue Client / Vuetify Client
– React Client / React Hooks Client
– React Image Upload with Preview
– Material UI Client
– Axios Client
For multiple Files at once:
How to upload multiple files in Node.js
Upload to GCS:
Google Cloud Storage with Node.js: File Upload example
You can also know way to upload an Excel file and store the content in MySQL database with the post:
Node.js: Upload/Import Excel file data into Database
If you want to upload images into database, you can find instructions at:
– Upload/store images in MySQL using Node.js, Express & Multer
– How to upload/store images in MongoDB using Node.js, Express & Multer
Happy Learning! See you again.
Further Reading
Source Code
You can find the complete source code for this tutorial on Github.
Thank you for the tutorial
Correct me if I am wrong or if I misunderstand, but it looks like the file being uploaded must ALREADY exist under /resources/static/assets/uploads on the machine where server.js is running?
How would we upload a file from the client (not the server) filesystem if this is so?
Is it also possible to upload a file to MYSQL DB instead of uploading it in the project folder ?
Hi, you can read this tutorial: Upload/store images in MySQL using Node.js, Express & Multer
Thank you very much for the tutorial, it is helpful.
I would like to know how to add a progress bar and suspend a download.
Hello, thank you for your tutorial, I really enjoy it ! I have no problem for get method when I upload a file manually but I have one for the post method with postman, the consol displays :
MulterError: Unexpected field
at wrappedFileFilter (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\multer\index.js:40:19)
at Busboy. (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\multer\lib\make-middleware.js:114:7)
at Busboy.emit (events.js:376:20)
at Busboy.emit (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\busboy\lib\main.js:38:33)
at PartStream. (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\busboy\lib\types\multipart.js:213:13)
at PartStream.emit (events.js:376:20)
at HeaderParser. (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\dicer\lib\Dicer.js:51:16)
at HeaderParser.emit (events.js:376:20)
at HeaderParser._finish (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\dicer\lib\HeaderParser.js:68:8)
at SBMH. (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\dicer\lib\HeaderParser.js:40:12)
at SBMH.emit (events.js:376:20)
at SBMH._sbmh_feed (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\streamsearch\lib\sbmh.js:159:14)
at SBMH.push (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\streamsearch\lib\sbmh.js:56:14)
at HeaderParser.push (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\dicer\lib\HeaderParser.js:46:19)
at Dicer._oninfo (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\dicer\lib\Dicer.js:197:25)
at SBMH. (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\dicer\lib\Dicer.js:127:10)
at SBMH.emit (events.js:376:20)
at SBMH._sbmh_feed (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\streamsearch\lib\sbmh.js:159:14)
at SBMH.push (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\streamsearch\lib\sbmh.js:56:14)
at Dicer._write (C:\Users\CTX2102\Documents\Site attirail\api\node_modules\dicer\lib\Dicer.js:109:17)
at writeOrBuffer (internal/streams/writable.js:358:12)
at Dicer.Writable.write (internal/streams/writable.js:303:10) {
code: ‘LIMIT_UNEXPECTED_FILE’,
storageErrors: [ ]
}
(node:35720) UnhandledPromiseRejectionWarning: TypeError: Cannot read property ‘originalname’ of undefined
at upload (C:\Users\CTX2102\Documents\Site attirail\api\app\Categorie\controllers\file.controller.js:28:61)
Sorry it very long.. But I don’t know what to do..
`url: baseURL + file` for the same reason they asked about `const fs = require(‘fs’)`
The import of `baseURL` is not included in the script on this page. So, the reader cant see that baseURL is available in the space.
Looking at the github, it is there but it isnt listed that the code here is incomplete (and everything else is complete). So, this is kind of confusing.
Hi, thank you for your comment. I just tried to make the code lean (with three dots […]) for reducing noise 🙂
For those who confuse with compile error or absent parts, please view full code on Github.
Maybe the best resource in the internet for file uploading and authentication in React, thank you for your excellent work and share, you save me and others an invaluable amount of time
Hi, thank you for your comment. 🙂
I am so happy to know that my work helps people like you.
I have download your code in github but when i run your code in get in browser this error :
cannot get /
Please help me.
Hi, this is Rest API that exports api endpoints for HTTP Client.
You need to build frontend application or Client app like Postman to work with it.
Hello, the tutorial is in fact awesome for me, well, keep up the good work fellows.
BeZKoder is a champion and modern-day hero. Thank you!
Is it also possible to upload a file to MongoDB instead of in the node.js folder?
Hi, you can find instruction at: How to upload/store images in MongoDB using Node.js, Express & Multer
I’ve made the Angular side for this node.js.
When clicking on a file to download it from the file list, I get sent to Localhost:8080FILENAME instead of Localhost:8080/files/FILENAME.
How do I make sure that clicking on a file name, takes you to the right url? In what .js file should this be adjusted or where can I find this mistake?
Thanks!
Hi,
In routes/index.js:
In controller/file.controller.js:
Hi friends,
It is very usefull to complete/Create REST API in node.
If possible can any one help on how to use REST API to upload a file.
I mean as a REST API client how can I upload file to the REST API. Sample code.
Thanks
Hi, you can find it here: Node.js Express File Upload Rest API example using Multer
Excellent, great, wonderful, friendly, easy, nice tutorial.
You are life saver all in one.
Thank you so much.
Hi!
First of all, thank you very much for the work you do. This has been very helpful to me.
In the file.controller.js I think you may be missing the const fs = require(‘fs’); in getListFiles. Also, in that function you use url: baseUrl + file where it should be url: directoryPath + file,
Regards!
Hi,
const fs = require(‘fs’);
does exists in file.controller.js (Github source code).You can see it here: https://github.com/bezkoder/express-file-upload/blob/master/src/controller/file.controller.js
The reason why I don’t write it in the tutorial is to make it straightforward.
Why
url: baseUrl + file
?I want to export API endpoint:
http://localhost:8080/files/[filename]
.