DEV Community

Cover image for How to save images to MongoDB in Node.js
collegewap
collegewap

Posted on • Originally published at codingdeft.com

How to save images to MongoDB in Node.js

In this article, we will see how to create APIs to upload an image and store them in MongoDB, list the images and download an image.

Project setup

Create a directory called node-mongo-image-upload and run the command npm run init inside it.
This will initiate a Node.js project.

Install the following dependencies:

npm i express multer dotenv multer-gridfs-storage
Enter fullscreen mode Exit fullscreen mode
  • express to create APIs is Node.js.
  • multer to upload files.
  • dotenv - to store configurations like database URL.
  • multer-gridfs-storage to save the file uploaded via multer to MongoDB.

Create .env file and add the MongoDB URL in it:

MONGO_DB_URL=mongodb://localhost:27017/images
Enter fullscreen mode Exit fullscreen mode

Uploading file

Create a file named index.js with the following code:

const express = require("express")
const multer = require("multer")
const { GridFsStorage } = require("multer-gridfs-storage")
require("dotenv").config()

const url = process.env.MONGO_DB_URL

// Create a storage object with a given configuration
const storage = new GridFsStorage({
  url,
  file: (req, file) => {
    //If it is an image, save to photos bucket
    if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
      return {
        bucketName: "photos",
        filename: `${Date.now()}_${file.originalname}`,
      }
    } else {
      //Otherwise save to default bucket
      return `${Date.now()}_${file.originalname}`
    }
  },
})

// Set multer storage engine to the newly created object
const upload = multer({ storage })

const app = express()

app.post("/upload/image", upload.single("avatar"), (req, res) => {
  const file = req.file
  // Respond with the file details
  res.send({
    message: "Uploaded",
    id: file.id,
    name: file.filename,
    contentType: file.contentType,
  })
})

const server = app.listen(process.env.PORT || 8765, function () {
  const port = server.address().port

  console.log("App started at port:", port)
})
Enter fullscreen mode Exit fullscreen mode

In the above code,

  • We are initializing the GridFs with the bucket name photos, whenever an image is uploaded, it will be saved to this bucket.
  • We are prefixing the file name with the date so that, each time a unique name is generated.
  • If it is not an image, then it will be saved to the default bucket called fs.
  • We have written a post route /upload/image, which will call the upload middleware.
  • The upload middleware will save the file sent at the parameter avatar.
  • Finally, we are responding with the uploaded file details.

Now start the application by running node index.js or nodemon.

Now in Postman, post an image to the endpoint http://localhost:8765/upload/image

Uploading a file in postman

If you click 'Send', you will receive uploaded image details in the response:

upload image postman

Now if you go and check the MongoDB, you will see the following collections created there:

mongo collections

fs.files and fs.chucks will be created when you upload files that are not images.

The photos.files collection will have the information about the uploaded images:

photos files

The photos.chunks collection will have the actual image data:

photos chunks

If the file size is larger, it will break into multiple chunks.

Listing uploaded files

You can list all the images by querying photos.files collection:

const express = require("express")
const multer = require("multer")
const { GridFsStorage } = require("multer-gridfs-storage")
require("dotenv").config()
const MongoClient = require("mongodb").MongoClient

const url = process.env.MONGO_DB_URL

const mongoClient = new MongoClient(url)

// Create a storage object with a given configuration
const storage = new GridFsStorage({
  url,
  file: (req, file) => {
    //If it is an image, save to photos bucket
    if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
      return {
        bucketName: "photos",
        filename: `${Date.now()}_${file.originalname}`,
      }
    } else {
      //Otherwise save to default bucket
      return `${Date.now()}_${file.originalname}`
    }
  },
})

// Set multer storage engine to the newly created object
const upload = multer({ storage })

const app = express()

app.post("/upload/image", upload.single("avatar"), (req, res) => {
  const file = req.file
  // Respond with the file details
  res.send({
    message: "Uploaded",
    id: file.id,
    name: file.filename,
    contentType: file.contentType,
  })
})

app.get("/images", async (req, res) => {
  try {
    await mongoClient.connect()

    const database = mongoClient.db("images")
    const images = database.collection("photos.files")
    const cursor = images.find({})
    const count = await cursor.count()
    if (count === 0) {
      return res.status(404).send({
        message: "Error: No Images found",
      })
    }

    const allImages = []

    await cursor.forEach(item => {
      allImages.push(item)
    })

    res.send({ files: allImages })
  } catch (error) {
    console.log(error)
    res.status(500).send({
      message: "Error Something went wrong",
      error,
    })
  }
})

const server = app.listen(process.env.PORT || 8765, function () {
  const port = server.address().port

  console.log("App started at port:", port)
})
Enter fullscreen mode Exit fullscreen mode

If you access http://localhost:8765/images via postman or browser, you should be able to see the list of image details.

list all images

Downloading an image

You can download an image by providing its name using the below code:

const express = require("express")
const multer = require("multer")
const { GridFsStorage } = require("multer-gridfs-storage")
require("dotenv").config()
const MongoClient = require("mongodb").MongoClient
const GridFSBucket = require("mongodb").GridFSBucket

const url = process.env.MONGO_DB_URL

const mongoClient = new MongoClient(url)

// Create a storage object with a given configuration
const storage = new GridFsStorage({
  url,
  file: (req, file) => {
    //If it is an image, save to photos bucket
    if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {
      return {
        bucketName: "photos",
        filename: `${Date.now()}_${file.originalname}`,
      }
    } else {
      //Otherwise save to default bucket
      return `${Date.now()}_${file.originalname}`
    }
  },
})

// Set multer storage engine to the newly created object
const upload = multer({ storage })

const app = express()

app.post("/upload/image", upload.single("avatar"), (req, res) => {
  const file = req.file
  // Respond with the file details
  res.send({
    message: "Uploaded",
    id: file.id,
    name: file.filename,
    contentType: file.contentType,
  })
})

app.get("/images", async (req, res) => {
  try {
    await mongoClient.connect()

    const database = mongoClient.db("images")
    const images = database.collection("photos.files")
    const cursor = images.find({})
    const count = await cursor.count()
    if (count === 0) {
      return res.status(404).send({
        message: "Error: No Images found",
      })
    }

    const allImages = []

    await cursor.forEach(item => {
      allImages.push(item)
    })

    res.send({ files: allImages })
  } catch (error) {
    console.log(error)
    res.status(500).send({
      message: "Error Something went wrong",
      error,
    })
  }
})

app.get("/download/:filename", async (req, res) => {
  try {
    await mongoClient.connect()

    const database = mongoClient.db("images")

    const imageBucket = new GridFSBucket(database, {
      bucketName: "photos",
    })

    let downloadStream = imageBucket.openDownloadStreamByName(
      req.params.filename
    )

    downloadStream.on("data", function (data) {
      return res.status(200).write(data)
    })

    downloadStream.on("error", function (data) {
      return res.status(404).send({ error: "Image not found" })
    })

    downloadStream.on("end", () => {
      return res.end()
    })
  } catch (error) {
    console.log(error)
    res.status(500).send({
      message: "Error Something went wrong",
      error,
    })
  }
})

const server = app.listen(process.env.PORT || 8765, function () {
  const port = server.address().port

  console.log("App started at port:", port)
})
Enter fullscreen mode Exit fullscreen mode

If you access the URL http://localhost:8765/download/<image_name>, you should be able to see the image.

download image

Source code

You can download the complete source code from here.

Top comments (0)