Table of Contents
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:
1npm i express multer dotenv multer-gridfs-storage
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:
1MONGO_DB_URL=mongodb://localhost:27017/images
Uploading file
Create a file named index.js
with the following code:
1const express = require("express")2const multer = require("multer")3const { GridFsStorage } = require("multer-gridfs-storage")4require("dotenv").config()56const url = process.env.MONGO_DB_URL78// Create a storage object with a given configuration9const storage = new GridFsStorage({10 url,11 file: (req, file) => {12 //If it is an image, save to photos bucket13 if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {14 return {15 bucketName: "photos",16 filename: `${Date.now()}_${file.originalname}`,17 }18 } else {19 //Otherwise save to default bucket20 return `${Date.now()}_${file.originalname}`21 }22 },23})2425// Set multer storage engine to the newly created object26const upload = multer({ storage })2728const app = express()2930app.post("/upload/image", upload.single("avatar"), (req, res) => {31 const file = req.file32 // Respond with the file details33 res.send({34 message: "Uploaded",35 id: file.id,36 name: file.filename,37 contentType: file.contentType,38 })39})4041const server = app.listen(process.env.PORT || 8765, function () {42 const port = server.address().port4344 console.log("App started at port:", port)45})
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
If you click 'Send', you will receive uploaded image details in the response:
Now if you go and check the MongoDB, you will see the following collections created there:
fs.files
andfs.chucks
will be created when you upload files that are not images.
The photos.files
collection will have the information about the uploaded images:
The photos.chunks
collection will have the actual image data:
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:
1const express = require("express")2const multer = require("multer")3const { GridFsStorage } = require("multer-gridfs-storage")4require("dotenv").config()5const MongoClient = require("mongodb").MongoClient67const url = process.env.MONGO_DB_URL89const mongoClient = new MongoClient(url)1011// Create a storage object with a given configuration12const storage = new GridFsStorage({13 url,14 file: (req, file) => {15 //If it is an image, save to photos bucket16 if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {17 return {18 bucketName: "photos",19 filename: `${Date.now()}_${file.originalname}`,20 }21 } else {22 //Otherwise save to default bucket23 return `${Date.now()}_${file.originalname}`24 }25 },26})2728// Set multer storage engine to the newly created object29const upload = multer({ storage })3031const app = express()3233app.post("/upload/image", upload.single("avatar"), (req, res) => {34 const file = req.file35 // Respond with the file details36 res.send({37 message: "Uploaded",38 id: file.id,39 name: file.filename,40 contentType: file.contentType,41 })42})4344app.get("/images", async (req, res) => {45 try {46 await mongoClient.connect()4748 const database = mongoClient.db("images")49 const images = database.collection("photos.files")50 const cursor = images.find({})51 const count = await cursor.count()52 if (count === 0) {53 return res.status(404).send({54 message: "Error: No Images found",55 })56 }5758 const allImages = []5960 await cursor.forEach(item => {61 allImages.push(item)62 })6364 res.send({ files: allImages })65 } catch (error) {66 console.log(error)67 res.status(500).send({68 message: "Error Something went wrong",69 error,70 })71 }72})7374const server = app.listen(process.env.PORT || 8765, function () {75 const port = server.address().port7677 console.log("App started at port:", port)78})
If you access http://localhost:8765/images
via postman or browser, you should be able to see the list of image details.
Downloading an image
You can download an image by providing its name using the below code:
1const express = require("express")2const multer = require("multer")3const { GridFsStorage } = require("multer-gridfs-storage")4require("dotenv").config()5const MongoClient = require("mongodb").MongoClient6const GridFSBucket = require("mongodb").GridFSBucket78const url = process.env.MONGO_DB_URL910const mongoClient = new MongoClient(url)1112// Create a storage object with a given configuration13const storage = new GridFsStorage({14 url,15 file: (req, file) => {16 //If it is an image, save to photos bucket17 if (file.mimetype === "image/jpeg" || file.mimetype === "image/png") {18 return {19 bucketName: "photos",20 filename: `${Date.now()}_${file.originalname}`,21 }22 } else {23 //Otherwise save to default bucket24 return `${Date.now()}_${file.originalname}`25 }26 },27})2829// Set multer storage engine to the newly created object30const upload = multer({ storage })3132const app = express()3334app.post("/upload/image", upload.single("avatar"), (req, res) => {35 const file = req.file36 // Respond with the file details37 res.send({38 message: "Uploaded",39 id: file.id,40 name: file.filename,41 contentType: file.contentType,42 })43})4445app.get("/images", async (req, res) => {46 try {47 await mongoClient.connect()4849 const database = mongoClient.db("images")50 const images = database.collection("photos.files")51 const cursor = images.find({})52 const count = await cursor.count()53 if (count === 0) {54 return res.status(404).send({55 message: "Error: No Images found",56 })57 }5859 const allImages = []6061 await cursor.forEach(item => {62 allImages.push(item)63 })6465 res.send({ files: allImages })66 } catch (error) {67 console.log(error)68 res.status(500).send({69 message: "Error Something went wrong",70 error,71 })72 }73})7475app.get("/download/:filename", async (req, res) => {76 try {77 await mongoClient.connect()7879 const database = mongoClient.db("images")8081 const imageBucket = new GridFSBucket(database, {82 bucketName: "photos",83 })8485 let downloadStream = imageBucket.openDownloadStreamByName(86 req.params.filename87 )8889 downloadStream.on("data", function (data) {90 return res.status(200).write(data)91 })9293 downloadStream.on("error", function (data) {94 return res.status(404).send({ error: "Image not found" })95 })9697 downloadStream.on("end", () => {98 return res.end()99 })100 } catch (error) {101 console.log(error)102 res.status(500).send({103 message: "Error Something went wrong",104 error,105 })106 }107})108109const server = app.listen(process.env.PORT || 8765, function () {110 const port = server.address().port111112 console.log("App started at port:", port)113})
If you access the URL http://localhost:8765/download/<image_name>
, you should be able to see the image.
Source code
You can download the complete source code from here.
Do follow me on twitter where I post developer insights more often!