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
-
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
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)
})
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:
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)
})
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:
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)
})
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.
Top comments (0)