How to upload/store images in MongoDB using Node.js, Express & Multer

In this tutorial, I will show you how to upload/store images in MongoDB using Node.js & Express with the help of multer & multer-gridfs-storage.

Related Posts:
How to upload multiple files in Node.js
Upload & resize multiple images in Node.js using Express, Multer, Sharp
Node.js Express File Upload Rest API (static folder) example using Multer
Google Cloud Storage with Node.js: File Upload example

More Practice:
Node.js, Express & MongoDb: Build a CRUD Rest Api example
Node.js + MongoDB: User Authentication & Authorization with JWT

Deployment: Docker Compose: Node.js Express and MongoDB example


Overview

Our Node.js Application will provide APIs for:

  • uploading Files/Images to MongoDB
  • getting list of Files’ information (file name & url)
  • downloading File/Image from server with the url

This is the UI:

node-upload-images-mongodb-ui

You can use an HTTP Client for image upload:

node-upload-images-mongodb-rest-api-upload-file

The images will be stored in 2 collections: photo.files and photos.chunks.

node-upload-images-mongodb-collection-files

node-upload-images-mongodb-collection-chunks

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

node-upload-images-mongodb-rest-api

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

These are APIs to be exported:

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

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
Axios Client

Node.js upload/store image in MongoDB

We’re gonna show you how to build this Node.js app step by step.

Project Structure

Now look at our project structure:

node-upload-images-mongodb-project-structure

config/db.js: includes configuration for MongoDB and Multer (url, database, image Bucket).
views/index.html: contains HTML form for user to upload images.
routes/index.js: defines routes for endpoints that is called from views, use controllers to handle requests.
controllers:

  • home.js returns views/index.html
  • upload.js handles upload, store, display and download images

middleware/upload.js: initializes Multer GridFs Storage engine (including MongoDB) and defines middleware function.
server.js: initializes routes, configure CORS, runs Express app.

Setup Node.js modules

Open command prompt, change current directory to the root folder of our project.
Install Express, CORS, Multer, Multer GridFs Storage and MongoDB with the following command:

npm install express cors multer multer-gridfs-storage mongodb

The package.json file will look like this:

{
  "name": "upload-multiple-images-mongodb",
  "version": "1.0.0",
  "description": "Node.js upload multiple files/images to MongoDB Demo",
  "main": "src/server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "node",
    "upload",
    "multiple",
    "files",
    "images",
    "mongodb"
  ],
  "author": "bezkoder",
  "license": "ISC",
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "mongodb": "^4.1.3",
    "multer": "^1.4.3",
    "multer-gridfs-storage": "^5.0.2"
  }
}

Create View for uploading image

In views folder, create index.html file with the HTML and Javascript code as below:

<!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 images</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 images - bezkoder.com</h4>
          <form class="mt-4"
            action="/upload"
            method="POST"
            enctype="multipart/form-data"
          >
            <div class="form-group">
              <input
                type="file"
                name="file"
                id="input-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-files").on("change", function() {
          imagesPreview(this, "div.preview-images");
        });
      });
    </script>
  </body>
</html>

For HTML part, we create a form with following elements:

  • action="/upload"
  • method="POST"
  • enctype="multipart/form-data"

You also need to notice the input tag with the name="file" attribute that we will use in the middleware.

The jQuery script shows preview of the chosen images.
We also use Bootstrap to make the UI more comfortable to read.

Configure MongoDB database

config/db.js

module.exports = {
  url: "mongodb://localhost:27017/",
  database: "bezkoder_files_db",
  imgBucket: "photos",
};

Create middleware for uploading & storing image

Inside middleware folder, create upload.js file with the following code:

const util = require("util");
const multer = require("multer");
const { GridFsStorage } = require("multer-gridfs-storage");
const dbConfig = require("../config/db");
var storage = new GridFsStorage({
  url: dbConfig.url + dbConfig.database,
  options: { useNewUrlParser: true, useUnifiedTopology: true },
  file: (req, file) => {
    const match = ["image/png", "image/jpeg"];
    if (match.indexOf(file.mimetype) === -1) {
      const filename = `${Date.now()}-bezkoder-${file.originalname}`;
      return filename;
    }
    return {
      bucketName: dbConfig.imgBucket,
      filename: `${Date.now()}-bezkoder-${file.originalname}`
    };
  }
});
var uploadFiles = multer({ storage: storage }).single("file");
var uploadFilesMiddleware = util.promisify(uploadFiles);
module.exports = uploadFilesMiddleware;

– We define a storage configuration object with GridFsStorage class.

  • url: must be a standard MongoDB connection string pointing to the MongoDB database. multer-gridfs-storage module will create a mongodb connection for you automatically.
  • options: customizes how to establish the connection, specified in the MongoClient.connect documentation.
  • file: this is the function to control the file storage in the database. The return value of this function is an object with the properties such as: filename, metadata, chunkSize, bucketName, contentType… 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 in MongoDB collection. bucketName indicates that the file will be stored at photos.chunks and photos.files collections.

– Next we use multer module to initialize middleware and util.promisify() to make the exported middleware object can be used with async-await.

– The single() function with the parameter is the name of input tag (in html view: <input type="file" name="file">) will store the single file in req.file.

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 Controller for the view

controllers/home.js

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

Create Controller for uploading Images

– 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 Image File Information and Download:

  • getListFiles(): read all files in MongoDB collection photos.files, return list of files’ information (name, url)
  • download(): receives file name as input parameter, open download Stream from mongodb built-in GridFSBucket, then response.write(chunk) API to transfer the file to the client.

controllers/upload.js

const upload = require("../middleware/upload");
const dbConfig = require("../config/db");
const MongoClient = require("mongodb").MongoClient;
const GridFSBucket = require("mongodb").GridFSBucket;
const url = dbConfig.url;
const baseUrl = "http://localhost:8080/files/";
const mongoClient = new MongoClient(url);
const uploadFiles = async (req, res) => {
  try {
    await upload(req, res);
    console.log(req.file);
    if (req.file == undefined) {
      return res.send({
        message: "You must select a file.",
      });
    }
    return res.send({
      message: "File has been uploaded.",
    });
  } catch (error) {
    console.log(error);
    return res.send({
      message: "Error when trying upload image: ${error}",
    });
  }
};
const getListFiles = async (req, res) => {
  try {
    await mongoClient.connect();
    const database = mongoClient.db(dbConfig.database);
    const images = database.collection(dbConfig.imgBucket + ".files");
    const cursor = images.find({});
    if ((await cursor.count()) === 0) {
      return res.status(500).send({
        message: "No files found!",
      });
    }
    let fileInfos = [];
    await cursor.forEach((doc) => {
      fileInfos.push({
        name: doc.filename,
        url: baseUrl + doc.filename,
      });
    });
    return res.status(200).send(fileInfos);
  } catch (error) {
    return res.status(500).send({
      message: error.message,
    });
  }
};
const download = async (req, res) => {
  try {
    await mongoClient.connect();
    const database = mongoClient.db(dbConfig.database);
    const bucket = new GridFSBucket(database, {
      bucketName: dbConfig.imgBucket,
    });
    let downloadStream = bucket.openDownloadStreamByName(req.params.name);
    downloadStream.on("data", function (data) {
      return res.status(200).write(data);
    });
    downloadStream.on("error", function (err) {
      return res.status(404).send({ message: "Cannot download the Image!" });
    });
    downloadStream.on("end", () => {
      return res.end();
    });
  } catch (error) {
    return res.status(500).send({
      message: error.message,
    });
  }
};
module.exports = {
  uploadFiles,
  getListFiles,
  download,
};

Define routes

In routes folder, define routes in index.js with Express Router.

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("/upload", uploadController.uploadFiles);
  router.get("/files", uploadController.getListFiles);
  router.get("/files/:name", uploadController.download);
  return app.use("/", router);
};
module.exports = routes;

There are 4 routes:
– GET: Home page for the upload form.
– POST "/upload" to call the uploadFiles function of the controller. This is also for action="/upload" in the view.
– GET /files for list of Images.
– GET /files/:name to download the image with the file name.

Create Express app server

Finally, we create an Express server.

server.js

const cors = require("cors");
const express = require("express");
const app = express();
const initRoutes = require("./routes");
var corsOptions = {
  origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
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 & Check result

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

The console shows:

Running at localhost:8080

Open browser with url http://localhost:8080/.

node-js-upload-store-images-mongodb-upload-single-image

Click on Submit button, if the file is uploaded and stored in MongoDB successfully, the console shows image’s information:

{ fieldname: 'file',
  originalname: 'extreme.jpg',
  encoding: '7bit',
  mimetype: 'image/jpeg',
  id: 5dd60e6aee27a520ccad86a0,
  filename: '1574309482432-bezkoder-extreme.jpg',
  metadata: null,
  bucketName: 'photos',
  chunkSize: 261120,
  size: 89431,
  md5: '0d072efbb134b3b186c5da705e0e7059',       
  uploadDate: 2019-11-21T04:11:23.264Z,
  contentType: 'image/jpeg' }

Check MongoDB database, you will see bezkoder_files_db with 2 collections: photos.chunks & photo.files:

node-js-upload-store-images-mongodb-demo-single-image-db-result

Node.js upload/store multiple images in MongoDB

Now I will show you how to modify some code to deal with multiple images instead of only one image at a time.

Change form for uploading multiple images

For the form, we write the input tag with new multiple attribute like this.

views/index.html

<input
  type="file"
  name="file"
  multiple
  id="input-files"
  class="form-control-file border"
/>

Modify middleware for uploading & storing image

For multiple images, we use another function: array() instead of single().

middleware/upload.js

...
// var uploadFile = multer({ storage: storage }).single("file");
var uploadFiles = multer({ storage: storage }).array("file", 10);
var uploadFilesMiddleware = util.promisify(uploadFiles);
module.exports = uploadFilesMiddleware;

array() function limits the number of files to upload each time, the first parameter is the name of – input tag (in html view: <input type="file" name="file">), the second parameter is the max number of files (10).

Controller for uploading multiple images

With the controller, we update the uploadFiles function.

controllers/upload.js

const uploadFiles = async (req, res) => {
  try {
    await upload(req, res);
    console.log(req.files);
    if (req.files.length <= 0) {
      return res
        .status(400)
        .send({ message: "You must select at least 1 file." });
    }
    return res.status(200).send({
      message: "Files have been uploaded.",
    });
    // console.log(req.file);
    // if (req.file == undefined) {
    //   return res.send({
    //     message: "You must select a file.",
    //   });
    // }
    // return res.send({
    //   message: "File has been uploaded.",
    // });
  } catch (error) {
    console.log(error);
    if (error.code === "LIMIT_UNEXPECTED_FILE") {
      return res.status(400).send({
        message: "Too many files to upload.",
      });
    }
    return res.status(500).send({
      message: `Error when trying upload many files: ${error}`,
    });
    // return res.send({
    //   message: "Error when trying upload image: ${error}",
    // });
  }
};

There is a additional part in the catch() block. We check if the error.code is "LIMIT_UNEXPECTED_FILE" for showing user the message when he tries to upload more than 10 images/files.

Don't forget to change the controller method on router:

router.post("/upload", uploadController.uploadFiles);

Run & Check result

Run the command: node src/server.js

And the console shows:

Running at localhost:8080

Open your browser with url http://localhost:8080/, add some images like this:

node-js-upload-store-images-mongodb-upload-multiple-images

Click on Submit button. If these images are uploaded and stored in MongoDB successfully, you can see:

node-js-upload-store-images-mongodb-demo-multiple-images-successful

The console shows these images' information:

[ { fieldname: 'multi-files',
    originalname: 'circle.png',
    encoding: '7bit',
    mimetype: 'image/png',
    id: 5dd637361ae0143fdc572105,
    filename: '1574319926050-bezkoder-circle.png',  
    metadata: null,
    bucketName: 'photos',
    chunkSize: 261120,
    size: 193593,
    md5: 'f6455d94e5ec26a1b6eb75b334220cbd',        
    uploadDate: 2019-11-21T07:05:30.273Z,
    contentType: 'image/png' },
  { fieldname: 'multi-files',
    originalname: 'JavaDuke.png',
    encoding: '7bit',
    mimetype: 'image/png',
    id: 5dd637361ae0143fdc572106,
    filename: '1574319926110-bezkoder-JavaDuke.png',    metadata: null,
    bucketName: 'photos',
    chunkSize: 261120,
    size: 112767,
    md5: 'b7f0fa8ea1932850d99a64b35484484a',        
    uploadDate: 2019-11-21T07:05:30.275Z,
    contentType: 'image/png' },
  { fieldname: 'multi-files',
    originalname: 'lotus.jpg',
    encoding: '7bit',
    mimetype: 'image/jpeg',
    id: 5dd637361ae0143fdc572107,
    filename: '1574319926121-bezkoder-lotus.jpg',   
    metadata: null,
    bucketName: 'photos',
    chunkSize: 261120,
    size: 375719,
    md5: '26dc2e512d7c021daaa7cb46215d4a5b',        
    uploadDate: 2019-11-21T07:05:32.326Z,
    contentType: 'image/jpeg' } ]

Check MongoDB database:
- photos.files collection:

node-js-upload-store-images-mongodb-demo-multiple-images-db-result-1

- chunks.files collection:

node-js-upload-store-images-mongodb-demo-multiple-images-db-result-2

If you try to upload more than 10 files at a time, you can see the error like this:

node-js-upload-store-images-mongodb-demo-multiple-images-error

Conclusion

Today we’ve learned how to upload and store single/multiple images in MongoDB database using express, multer & multer-gridfs-storage modules, way to limit the number of files by configuration.

We also know how to get list of uploaded files/images, and provide urls for download.

Happy learning! See you again.

Further Reading

Node.js & MongoDB Associations:

Fullstack CRUD App:

Deployment: Docker Compose: Node.js Express and MongoDB example

Source Code

You can find the complete source code for this tutorial at Github.

34 thoughts to “How to upload/store images in MongoDB using Node.js, Express & Multer”

  1. How can i submit other details such as name , time , price of a product(for example) along with the image??
    Any help would be appreciated

  2. Hello Sir,I am getting this Error: The database connection must be open to store files I have clone your code but still I am getting this error ,please help sir how can I solve this?

  3. In Middleware/upload.js
    const {GridFsStorage} = require(‘multer-gridfs-storage’);
    instead
    const GridFsStorage = require(“multer-gridfs-storage”);

  4. Hi,
    Thanks for your post.
    Can you help me config connect to mongoose online before upload the image?

  5. Thanks a ton bezkoder, super useful for my little app!

    Would it by any chance be possible to show us how to build a ‘get’ request and display on the webpage as well?

  6. What of I i want to use diskStorage and then save the url to mongodb can you please do a tutorial on that.. Or how do I do it

  7. Hello Bezkoder and many thanks for your tut.
    There is nothing more wonderful than if you could guide to read uploaded image and especially with image have many chunks(n: 0, n: 1, n:2). Big thanks

  8. I’m also getting an “unexpected field” error when I try to upload an image in post man. Help?

  9. I’m getting an error saying there is no such file or directory (can’t find my index.html file when I run the server).
    Help?

  10. Hello.

    Thank you for your effort! This is really helpfull.

    I’m trying to upload 2 images at once, and I need then to be nested in one single document. It should look like this:

    > {
    _id “xxxxx”,
    name: “xxxxxx”,
    age: “XX”,
    img1: FIRST_IMAGE_ID (probably a document),
    img2: SECOND_IMAGE_ID (probably a document)
    }

    Is that possible?

  11. Hi Bezkoder,

    Firstly, the information provided was really awesome!
    A question: Can we follow the same procedure If in case we have multiple inputs types like username, address along with image attachment? If there is any other way around please suggest.

    Thanks in advance!

  12. Dude your an inspiration! Without your tutorials maybe I give up learning database as of now,
    cause your tutorials are pretty straightforward. Thanks a Lot….

  13. hello bezkoder

    actually iam uploading image through node js and angular js but iam facing issue in controllers can you please help mee

  14. Thanks for this epic method.
    if you upload source code for this example i could be very use ful.

    1. Hi, all source code is shown in the steps. you only need to follow them and it will work 🙂

      1. Hi,Thanks a tonne to provide the source code with explanation.But its not working for me.It would be more helpful if you could provide package.json file as well would have been more useful for the new developers like me.

        1. Ah yeah, the package.json file will look like this:

          {
            "name": "upload-multiple-files",
            "version": "1.0.0",
            "description": "Node.js upload multiple files Demo",
            "main": "server.js",
            "scripts": {
              "test": "echo \"Error: no test specified\" && exit 1"
            },
            "keywords": [
              "node",
              "upload",
              "multiple",
              "files"
            ],
            "author": "bezkoder",
            "license": "ISC",
            "dependencies": {
              "express": "^4.17.1",
              "multer": "^1.4.2",
              "multer-gridfs-storage": "^4.0.1"
            }
          }
          

          I also updated the tutorial for this .json file. Thanks for your suggestion 🙂

  15. Thanks for your tutorial!
    This is exactly what I wanted to look for way to upload images to MongoDB.

  16. Thank you for this tutorial. Hope you write more useful Node.js & MongoDB tutorials in the future.

Comments are closed to reduce spam. If you have any question, please send me an email.