How to upload multiple files in Node.js

Upload multiple files in Node.js

Before going to the practice part, I’ll show you a couple of things to notice.

In HTML, we need a form with: – action="/multiple-upload"enctype="multipart/form-data"multiple input

<form action="/multiple-upload" method="POST" enctype="multipart/form-data">
    ...
    <input type="file" multiple>
    ...
</form>

With the action "/multiple-upload" and POST method, we use Express Router for the endpoint and controller to handle upload files:

const express = require("express");
const router = express.Router();
const uploadController = require("../controllers/upload");

let routes = app => {
  router.post("/multiple-upload", uploadController.multipleUpload);
  return app.use("/", router);
};

The controller calls a middleware that uses Multer disk storage engine:

var storage = multer.diskStorage({
  destination: function (req, file, callback) {
    callback(null, '/path/to/uploads')
  },
  filename: function (req, file, callback) {
    callback(null, file.fieldname + '-' + Date.now())
  }
});
 
var upload = multer({ storage: storage });

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.

Demo for upload multiple files

Open the app:

Choose the images to upload:

Click on Submit button, if the process is successful, you can see the files in upload folder:

If the number of files we choose is larger than 10 (I will show you how to set the limit later):

Practice

Now this is the time for building our app.

Project Structure

Look at the project structure:

You can see that we have these main parts: – upload: the folder for storing uploaded files. – views/index.html: contains HTML form for user to upload files. – routes/web.js: defines routes for endpoints that is called from views, use controllers to handle requests. – controllers: home.js to show views/index.html, upload.js to handle upload multiple files with middleware. – middleware/upload.js: initializes Multer disk storage engine. – server.js: initializes routes, runs Express app.

Setup Node.js modules

We need to install 2 packages: express and multer. So run the command: npm install express multer

Create view

Under views folder, create index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Node.js upload multiple files</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"/>
    <style>
      div.preview-images > img {
        width: 30%;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="row">
        <div class="col-sm-8 mt-3">
          <h4>Node.js upload multiple files - bezkoder.com</h4>

          <form action="/multiple-upload" method="POST" enctype="multipart/form-data">
            <div class="form-group">
              <label for="example-input-file"> </label>
              <input type="file" name="multi-files" multiple id="input-multi-files" class="form-control-file border"/>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
          </form>
        </div>
      </div>
      <hr />
      <div class="row">
        <div class="col-sm-12">
          <div class="preview-images"></div>
        </div>
      </div>
    </div>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.bundle.min.js"></script>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script>
      $(document).ready(function() {
        let imagesPreview = function(input, placeToInsertImagePreview) {
          if (input.files) {
            let filesAmount = input.files.length;
            for (i = 0; i < filesAmount; i++) {
              let reader = new FileReader();
              reader.onload = function(event) {
                $($.parseHTML("<img>"))
                  .attr("src", event.target.result)
                  .appendTo(placeToInsertImagePreview);
              };
              reader.readAsDataURL(input.files[i]);
            }
          }
        };
        $("#input-multi-files").on("change", function() {
          imagesPreview(this, "div.preview-images");
        });
      });
    </script>
  </body>
</html>

The script above helps us show preview of the chosen files. To make things simple, I don’t explain it deeply in the tutorial.

Create middleware for upload multiple files

Inside middleware folder, create upload.js:

const util = require("util");
const path = require("path");
const multer = require("multer");

var storage = multer.diskStorage({
  destination: (req, file, callback) => {
    callback(null, path.join(`${__dirname}/../../upload`));
  },
  filename: (req, file, callback) => {
    const match = ["image/png", "image/jpeg"];

    if (match.indexOf(file.mimetype) === -1) {
      var message = `${file.originalname} is invalid. Only accept png/jpeg.`;
      return callback(message, null);
    }

    var filename = `${Date.now()}-bezkoder-${file.originalname}`;
    callback(null, filename);
  }
});

var uploadFiles = multer({ storage: storage }).array("multi-files", 10);
var uploadFilesMiddleware = util.promisify(uploadFiles);
module.exports = uploadFilesMiddleware;

– We define a storage configuration object, then use multer module to initialize middleware and util.promisify() to make the exported middleware object can be used with async-await.

– To limit the number of files to upload each time, we use array() function with the first parameter is the name of input tag (in html view: <input type="file" name="multi-files">), the second parameter is the max number of files.

– We also check if the file is an image or not using file.mimetype. Then we add the [timestamp]-bezkoder- prefix to the file’s original name to make sure that the duplicates never occur.

If you want to do more, for example, resize the images, please visit this post: Upload & resize multiple images in Node.js using Express, Multer, Sharp

Create controllers

we create 2 files in controllers folder:

home.js

const path = require("path");

const home = (req, res) => {
  return res.sendFile(path.join(`${__dirname}/../views/index.html`));
};

module.exports = {
  getHome: home
};

upload.js

const upload = require("../middleware/upload");

const multipleUpload = async (req, res) => {
  try {
    await upload(req, res);
    console.log(req.files);

    if (req.files.length <= 0) {
      return res.send(`You must select at least 1 file.`);
    }

    return res.send(`Files has been uploaded.`);
  } catch (error) {
    console.log(error);

    if (error.code === "LIMIT_UNEXPECTED_FILE") {
      return res.send("Too many files to upload.");
    }
    return res.send(`Error when trying upload many files: ${error}`);
  }
};

module.exports = {
  multipleUpload: multipleUpload
};

Define routes

Under routes folder, define routes in web.js:

const express = require("express");
const router = express.Router();
const homeController = require("../controllers/home");
const uploadController = require("../controllers/upload");

let routes = app => {
  router.get("/", homeController.getHome);

  router.post("/multiple-upload", uploadController.multipleUpload);

  return app.use("/", router);
};

module.exports = routes;

There are 2 routes: - GET: Home page for the upload form. - POST "/multiple-upload" to call the upload controller.

Create Express app server

The last thing to do is creating an Express server.

server.js

const express = require("express");
const app = express();
const initRoutes = require("./routes/web");

app.use(express.urlencoded({ extended: true }));
initRoutes(app);

let port = 3000;
app.listen(port, () => {
  console.log(`Running at localhost:${port}`);
});

Run the app

On the project root folder, run this command: node src/server.js

The console shows:

Running at localhost:3000

Now you can open browser with url http://localhost:3000/ to check the result.

Last updated