Uploading Images on MongoDB via nodeJS

Uploading Images on MongoDB via nodeJS

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

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 uploadng 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