Uploading Images on MongoDB via nodeJS
Tags: javascript, node, mongodb
Published: 2020-10-31 11:49:30

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](#content-1)
* [Upload images to MongoDB](#content-2)
* [Get the list of image object (in an array)](#content-3)
* [Get a single image object](#content-4)
* [Display the actual image](#content-5)
* [Delete an image](#content-6)

### <a name="content-1">Setup the necessary tools</a>
Before we move forward, we are definitely going to need some packages from [NPM](https://www.npmjs.com/) (Node Package Manager), such has


* [Express](https://expressjs.com/) : basically a Node.js web application framework
* [Mongoose](https://mongoosejs.com/) : Object Data Modeling (ODM) library for MongoDB and Node.js. It basically handles relationship between data
* [Multer](https://www.npmjs.com/package/multer) : Is used for uploading files
* [Gridfs-stream](https://www.npmjs.com/package/gridfs-stream) : Allows streaming of files to and from mongodb
* [Gridfs](https://docs.mongodb.com/manual/core/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](https://www.mongodb.com/cloud/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 })
```

### <a name="content-2">Upload images to MongoDB</a> 
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.

### <a name="content-3">Get the list of image object (in an array)</a> 
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
### <a name="content-4">Get a single image object</a> 
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)
    })
})
```

### <a name="content-5">Display the actual image</a> 
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.

### <a name="content-6">Delete an image</a> 
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](https://twitter.com/fakoredeDami)