Images are very important on our websites and gives life to the internet. A website without one or two images will be very close to boring.
Images like content can be uploaded on a database for easy access, and today I am going to show you how to easily upload images to MongoDB via nodeJS.
Today I will explain how to
- Setup the necessary tools
- Upload images to MongoDB
- Get the list of image object (in an array)
- Get a single image object
- Display the actual image
- Delete an image
Setup the necessary tools
Before we move forward, we are definitely going to need some packages from NPM (Node Package Manager), such has
- Express : basically a Node.js web application framework
- Mongoose : Object Data Modeling (ODM) library for MongoDB and Node.js. It basically handles relationship between data
- Multer : Is used for uploading files
- Gridfs-stream : Allows streaming of files to and from mongodb
- Gridfs : This is a specification for storing and retriviing files that excess the BSON-document size limit of 16MB
npm i express mongoose multer multer-gridfs-storage gridfs-stream
We will be uploading straight to MongoDB atlas, which is a remote MongoDB database, you can also use the local instance but the connection string will be different.
The first thing is to import the required modules, and some core nodeJS modules, and just create the basic server
const express = require('express')
const path = require('path')
const crypto = require('crypto')//to generate file name
const mongoose = require('mongoose')
const multer = require('multer')
const GridFsStorage = require('multer-gridfs-storage')
const Grid = require('gridfs-stream')
const app = express()
// other code here
const PORT =5000
app.listen(PORT,()=>console.log(`Server started on port ${PORT}`))
Next is to add the connection string. If you are using the local instance , yours will probably be 27017...
const mongoURI = "mongodb+srv://fako:fako@nodejspassport-nahp0.mongodb.net"
Next thing is to, create a connection via mongoose, initialize a variable for stream(i.e gfs) and once the connection is open, set the gfs variable to Grid(gridfs-stream) and then pass the collection where our images will be stored to gfs :). Note that this collection will be divided into two, imageUpload.chunk and imageUpload.files
let conn = mongoose.connection
let gfs
conn.once('open', () => {
//initialize our stream
gfs = Grid(conn.db, mongoose.mongo)
gfs.collection('imageUpload')
})
Now, we are going to create a storage object with a given configuration.
The first property will be the uri string which we specified above and the second is called file, a function to control the file storage in the database. It is invoked per file with the parameters req and file in that order and returns an object of a promise that resolves to an object. Some of the property of the object include
filename : The desired filename for the file (default: 16 byte hex name without extension), but you can override this with your given name
content-type : The content type for the file (default: inferred from the request)
bucketname : The GridFs collection to store the file (default: fs)
missing property will use the default
let storage = new GridFsStorage({
url: uri,
file: (req, file) => {
return new Promise(
(resolve, reject) => {
const fileInfo = {
filename: file.originalname,
bucketName: "imageUpload"
}
resolve(fileInfo)
}
)
}
})
Set the multer storage engine to the newly created object, we will use this upload variable has our middleware, so that it actually upload to the database
const upload = multer({ storage })
Upload images to MongoDB
Now to actually upload an image. The upload variable will be added has a middleware and .single will be called on it (because we are uploading a single file each time. You can upload multiple files has an array). You will then pass the name you specified in your input field i.e in the frontend (e.g input type="file" name="upload"
app.post("/upload",upload.single("upload"),(req,res)=>{
res.json({file:req.file})
})
I am not really going to deal with the frontend in this article, but you should have a basic html file with an input file field that on submit will make an AJAX request to localhost:5000/upload, and if you try it out, that should work :). If you were to check atlas or your local database, and you should see the file uploaded.
Get the list of image object (in an array)
To get the list of image object is pretty straight forward,
app.get('/files', (req, res) => {
gfs.files.find().toArray((err, files) => {
//check if files exist
if (!files || files.length == 0) {
return res.status(404).json({
err: "No files exist"
})
}
// files exist
return res.json(files)
})
})
We are basically using gridfs-stream(gfs) like we will use mongoose. Go to the url with /files and you will see an array of the uploaded files
Get a single image object
To get a single file, all we need is the filename and we can call a findOne on gfs i.e
app.get('/files/:filename', (req, res) => {
gfs.files.findOne({ filename: req.params.filename }, (err, file) => {
//check if files exist
if (!file || file.length == 0) {
return res.status(404).json({
err: "No files exist"
})
}
//file exist
return res.json(file)
})
})
Display the actual image
To get the image itself,
app.get('/image/:filename', (req, res) => {
gfs.files.findOne({ filename: req.params.filename }, (err, file) => {
//check if files exist
if (!file || file.length == 0) {
return res.status(404).json({
err: "No files exist"
})
}
//check if image
if (file.contentType === 'image/jpeg' || file.contentType === "img/png") {
//read output to browser
const readStream = gfs.createReadStream(file.filename)
readStream.pipe(res)
} else {
res.status(404).json({
err: "Not an image"
})
}
})
})
The first thing to do is check if the file actually exists, if it does, go ahead and check if it is actually an image by looking at it contentType. If is actually an image, then read it to the browser by creating a readStream.
Delete an image
Deleting an image is just as easy, all you have to do is make a delete request i.e
app.delete("/files/:id", (req, res) => {
gfs.remove({ _id: req.params.id, root: 'imageUpload' }, (err, gridStore) => {
if (err) {
return res.status(404).json({ err: err })
}
res.redirect("/")
})
})
and that is how you upload an image to MongoDB via NodeJS. Thank you
You can follow me on twitter @fakoredeDami
Top comments (9)
Can you annotate the code what file is meant? Or share the source? Thank you!
Hello,
I really don't understand the file you mean, but there are basically three main file variables here. The file you get from the storage object has a property (this is from the multer-gridfs-storage), the file you pass has an argument (this is the file the user uploads. and you can get access to properties such has the originalname) and the file which I used has the property of an object which I sent back to the user(this is not really important,you can just easily redirect the person.
Hope that answers you question
Hello Sir i need your help
Hi, how can I be of assistance
"uploadng" should be "uploading"
Thank you :)
Hi, excellent post!
maybe can be a dumb ask but, how can manage these files be used?, because when I seek in Compass I don't see the "media" collection that should be created when I do the request from the front. I guess the upload variable doesn't work. Can you help me pls? thanks! (the name of the input is "media")
Code
dev-to-uploads.s3.amazonaws.com/up...
Will this work for other files than images if I change the image specification stuff?
A good article! Basic stuff but relevant and well written.