Node.js Express File Upload Rest API example using Multer

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 Client / Angular 10 Client / Angular 11 Client / Angular 12
Angular Material 12
Vue Client / Vuetify Client
React Client / React Hooks Client
Material UI Client

Related Posts:
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


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)

This is the static folder that stores all uploaded files:

upload-file-node-js-express-rest-api-file-upload-folder

If we get list of files, the Node.js Rest Apis will return:

upload-file-node-js-express-rest-api-file-upload-list-response

Each item in the response array has a url that you can use for downloading the file.

These are APIs to be exported:

MethodsUrlsActions
POST/uploadupload a File
GET/filesget List of Files (name & url)
GET/files/[filename]download a File

Technology

  • express 4.17.1
  • multer 1.4.2
  • cors 2.8.5

Project Structure

This is the project directory that we’re gonna build:

upload-file-node-js-express-rest-api-file-upload-project-structure

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’ informationn (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-file-node-js-express-rest-api-file-upload

– Upload a file with size larger than max file size (2MB):

upload-file-node-js-express-rest-api-file-upload-limit-size

– Check uploads folder after uploading several files:

upload-file-node-js-express-rest-api-file-upload-folder

– Retrieve list of Files’ information:

upload-file-node-js-express-rest-api-file-upload-list-response

– 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.

Following tutorials explain how to build Front-end Apps to work with our Node.js Express Server:
Angular 8 Client / Angular 10 Client / Angular 11 Client / Angular 12
Angular Material 12
Vue Client / Vuetify Client
React Client / React Hooks Client
Material UI Client
React Image Upload with Preview

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.

22 thoughts to “Node.js Express File Upload Rest API example using Multer”

  1. 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!

  2. 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

  3. 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!

    1. Hi,

      In routes/index.js:

      router.get("/files/:name", controller.download);
      

      In controller/file.controller.js:

      ...
      const baseUrl = "http://localhost:8080/files/";
      
      const getListFiles = (req, res) => {
          ...
          files.forEach((file) => {
            fileInfos.push({
              name: file,
              url: baseUrl + file,
            });
          });
          ...
      }
      
  4. 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

    1. Hi, thank you for your comment. 🙂
      I am so happy to know that my work helps people like you.

      1. I have download your code in github but when i run your code in get in browser this error :
        cannot get /

        Please help me.

        1. 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.

  5. `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.

    1. 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.

  6. 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..

  7. 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.

  8. 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?

Leave a Reply

Your email address will not be published. Required fields are marked *