How to use GridFS to store images using Node.js and Mongoose How to use GridFS to store images using Node.js and Mongoose mongoose mongoose

How to use GridFS to store images using Node.js and Mongoose


I was not satisfied with the highest rated answer here and so I'm providing a new one:I ended up using the node module 'gridfs-stream' (great documentation there!) which can be installed via npm.With it, and in combination with mongoose, it could look like this:

var fs = require('fs');var mongoose = require("mongoose");var Grid = require('gridfs-stream');var GridFS = Grid(mongoose.connection.db, mongoose.mongo);function putFile(path, name, callback) {    var writestream = GridFS.createWriteStream({        filename: name    });    writestream.on('close', function (file) {      callback(null, file);    });    fs.createReadStream(path).pipe(writestream);}

Note that path is the path of the file on the local system.

As for my read function of the file, for my case I just need to stream the file to the browser (using express):

try {    var readstream = GridFS.createReadStream({_id: id});    readstream.pipe(res);} catch (err) {    log.error(err);    return next(errors.create(404, "File not found."));}


Answers so far are good, however, I believe it would be beneficial to document here how to do this using the official mongodb nodejs driver instead of relying on further abstractions such as "gridfs-stream".

One previous answer has indeed utilized the official mongodb driver, however they use the Gridstore API; which has since been deprecated, see here. My example will be using the new GridFSBucket API.

The question is quite broad as such my answer will be an entire nodejs program. This will include setting up the express server, mongodb driver, defining the routes and handling the GET and POST routes.

Npm Packages Used

  • express (nodejs web application framework to simplify this snippet)
  • multer (for handling multipart/form-data requests)
  • mongodb (official mongodb nodejs driver)

The GET photo route takes a Mongo ObjectID as a parameter to retrieve the image.

I configure multer to keep the uploaded file in memory. This means the photo file will not be written to the file system at anytime, and instead be streamed straight from memory into GridFS.


/** * NPM Module dependencies. */const express = require('express');const photoRoute = express.Router();const multer = require('multer');var storage = multer.memoryStorage()var upload = multer({ storage: storage, limits: { fields: 1, fileSize: 6000000, files: 1, parts: 2 }});const mongodb = require('mongodb');const MongoClient = require('mongodb').MongoClient;const ObjectID = require('mongodb').ObjectID;let db;/** * NodeJS Module dependencies. */const { Readable } = require('stream');/** * Create Express server && Routes configuration. */const app = express();app.use('/photos', photoRoute);/** * Connect Mongo Driver to MongoDB. */MongoClient.connect('mongodb://localhost/photoDB', (err, database) => {  if (err) {    console.log('MongoDB Connection Error. Please make sure that MongoDB is running.');    process.exit(1);  }  db = database;});/** * GET photo by ID Route */photoRoute.get('/:photoID', (req, res) => {  try {    var photoID = new ObjectID(req.params.photoID);  } catch(err) {    return res.status(400).json({ message: "Invalid PhotoID in URL parameter. Must be a single String of 12 bytes or a string of 24 hex characters" });   }  let bucket = new mongodb.GridFSBucket(db, {    bucketName: 'photos'  });  let downloadStream = bucket.openDownloadStream(photoID);  downloadStream.on('data', (chunk) => {    res.write(chunk);  });  downloadStream.on('error', () => {    res.sendStatus(404);  });  downloadStream.on('end', () => {    res.end();  });});/** * POST photo Route */photoRoute.post('/', (req, res) => {  upload.single('photo')(req, res, (err) => {    if (err) {      return res.status(400).json({ message: "Upload Request Validation Failed" });    } else if(!req.body.name) {      return res.status(400).json({ message: "No photo name in request body" });    }        let photoName = req.body.name;        // Covert buffer to Readable Stream    const readablePhotoStream = new Readable();    readablePhotoStream.push(req.file.buffer);    readablePhotoStream.push(null);    let bucket = new mongodb.GridFSBucket(db, {      bucketName: 'photos'    });    let uploadStream = bucket.openUploadStream(photoName);    let id = uploadStream.id;    readablePhotoStream.pipe(uploadStream);    uploadStream.on('error', () => {      return res.status(500).json({ message: "Error uploading file" });    });    uploadStream.on('finish', () => {      return res.status(201).json({ message: "File uploaded successfully, stored under Mongo ObjectID: " + id });    });  });});app.listen(3005, () => {  console.log("App listening on port 3005!");});

I wrote a blog post on this subject; is is an elaboration of my answer. Available here

Further Reading/Inspiration:


I suggest taking a look at this question: Problem with MongoDB GridFS Saving Files with Node.JS

Copied example from the answer (credit goes to christkv):

// You can use an object id as well as filename nowvar gs = new mongodb.GridStore(this.db, filename, "w", {  "chunk_size": 1024*4,  metadata: {    hashpath:gridfs_name,    hash:hash,    name: name  }});gs.open(function(err,store) {  // Write data and automatically close on finished write  gs.writeBuffer(data, true, function(err,chunk) {    // Each file has an md5 in the file structure    cb(err,hash,chunk);  });});