MongoDB GridFS

In this tutorial, you will learn, how you can upload, retrieve, and delete files using MongoDB GridFS.

Video tutorial:

Upload the file to MongoDB GridFS

First, in your Node JS file, you need to create an instance of your GridFS bucket. You can create as many buckets as you want.

// include MongoDB
const mongodb = require("mongodb")

// get MongoDB client
const mongoClient = mongogb.MongoClient

// connect with MongoDB server
const client = await mongoClient.connect("mongodb://localhost:27017")

const db = client.db("mongodb_gridfs")

// create GridFS bucket instance
const bucket = new mongodb.GridFSBucket(db)
  1. First, it includes the MongoDB module.
  2. Then it gets a Mongo client object that helps in connecting with the database.
  3. Then we connect to the server.
  4. After that, we set the database.
  5. And finally, we are creating an instance of a bucket.

You can also give your bucket a name to identify.

// create GridFS bucket instance named "myBucketName"
const bucket = new mongodb.GridFSBucket(db, {
  bucketName: "myBucketName"
})

Following POST route will save the file.

// npm install fs
const fs = require("fs")

// npm install ejs
app.set("view engine", "ejs")

// npm install express-formidable
const expressFormidable = require("express-formidable")
app.use(expressFormidable())

app.post("/upload", function (request, result) {
  // get input name="file" from client side
  const file = request.files.file
  
  // set file path in MongoDB GriDFS
  // this will be saved as "filename" in "fs.files" collection
  const filePath = (new Date().getTime()) + "-" + file.name
  
  // read user uploaded file stream
  fs.createReadStream(file.path)
  
    // add GridFS bucket stream to the pipe
    // it will keep reading and saving file
    .pipe(
      bucket.openUploadStream(filePath, {
        // maximum size for each chunk (in bytes)
        chunkSizeBytes: 1048576, // 1048576 = 1 MB
        // metadata of the file
        metadata: {
          name: file.name, // file name
          size: file.size, // file size (in bytes)
          type: file.type // type of file
        }
      })
    )
    // this callback will be called when the file is done saving
    .on("finish", function () {
      result.send("File saved.")
    })
})

Now if you check in your “mongodb_gridfs” database, you will see 2 new collections.

  1. fs.files
    • This will save all uploaded files.
  2. fs.chunks
    • This will save all chunks of each file with that file ID.

Fetch all files from MongoDB GridFS

The following GET route will fetch all files uploaded to MongoDB GridFS.

app.get("/", async function (request, result) {
  const files = await bucket.find({
    // filename: "name of file" // 
  })
    .sort({
      uploadDate: -1
    })
    .toArray()
  result.render("index", {
    files: files
  })
})

Now you need to create a folder named “views” and inside that folder create a file named “index.ejs”.

Then you can loop through all files and display them in the image tag.

<% if (files) { %>
  <% files.forEach(function (file) { %>
    <p><%= file.filename %></p>
    <img src="image/<%= file.filename %>" style="width: 200px;" />
  <% }) %>
<% } %>

Right now, you will see a broken image. Now we need to create an API that will return the image as a response.

Return image as API response

app.get("/image/:filename", async function (request, result) {
  // get file name from URL
  const filename = request.params.filename
  
  // get file from GridFS bucket
  const files = await bucket.find({
    filename: filename
  })
  .toArray()
  
  // return error if file not found
  if (!files || files.length == 0) {
    return result.status(404).json({
      error: "File does not exists."
    })
  }
  
  // it will fetch the file from bucket and add it to pipe
  // result response is added in the pipe so it will keep
  // returning data to the client
  bucket.openDownloadStreamByName(filename)
    .pipe(result)
})

Now you will be able to view the image.

Delete file from MongoDB GridFS

First, you need to create a button after each file.

<% if (files) { %>
  <% files.forEach(function (file) { %>
    <p><%= file.filename %></p>
    <img src="image/<%= file.filename %>" style="width: 200px;" />
    
    <form action="/files/del" method="POST">
      <input type="hidden" name="_id" value="<%= file._id %>" />
      <button type="submit" class="btn btn-danger">Delete</button>
    </form>
  <% }) %>
<% } %>

Then you need to create an API in your Node JS file.

// 
const ObjectId = mongodb.ObjectId

app.post("/files/del", async function (request, result) {
  // get ID from data
  const _id = request.fields._id
  
  // delete file from bucket
  await bucket.delete(ObjectId(_id))
  
  // return response
  result.send("File has been deleted.")
})

This will delete the file and all its chunks from the database. So that’s all for now if you face any problem in following this, kindly do let me know.

You can learn more about the grid file system from Mongo DB’s official website.