I am new to Node.js. Can anyone provide me an example of how to use GridFS for storing and retrieving binary data, such as images, using Node.js and Mongoose? Do I need to directly access GridFS?
So for storing an image in MongoDB, we need to create a schema with mongoose. For that create the file `model. js` file and define the schema. The important point here is that our data type for the image is a Buffer which allows us to store our image as data in the form of arrays.
GridFS organizes files in a bucket, a group of MongoDB collections that contain the chunks of files and descriptive information. Buckets contain the following collections, named using the convention defined in the GridFS specification: The chunks collection stores the binary file chunks.
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With