Search code examples
node.jsmongodbgridfsgridfs-stream

How to get the filename of a mongodb GridFS file given its id?


What if I know the mongo id of a file stored in gridFS, and want to download it under the filename that is stored in its fs.files filename field? I don't have a model to call the collection with, so I'm confused how I'm supposed to do a query on that collection.

This is my server.js file, where I connect to the database:

require('dotenv').config() 
const mongoose = require('mongoose')

//connect to mongoose
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true })
    .then(() => {
        // Listen for requests
        app.listen(process.env.PORT, () => {
            console.log(`Connected to MongoDB and listening on port`, process.env.PORT)
        })
        
    })
    .catch((error) => {
        //throws error if the MONGO_URI string has the wrong user or pw
        console.log(error)
})

and in my files controllers file I have this:

 const mongoose = require('mongoose')
 require('dotenv').config()
 const fs = require('fs')

//Create a bucket for storing files
//bucket is initialized to fs and has 2 collections: fs.files and fs.chunks
let bucket
let conn
mongoose.connection.on("connected", () => {
    conn = mongoose.connections[0].db
    bucket = new mongoose.mongo.GridFSBucket(conn)
})

const downloadFileByID = async (req, res) => {
  const { id } = req.params

  if (!mongoose.Types.ObjectId.isValid(id)){
      return res.status(404).json({error: 'No such file'})
  }
  const mongooseID = new mongoose.Types.ObjectId(id)

  //Get filename
  const cursor = bucket.find({}) //find() returns a FindCursor iterable
  let filename
  for await (const file of cursor) {
    if (file._id == id){
      filename = file.filename
    }
  }

  try{
    bucket.openDownloadStream(mongooseID).pipe(fs.createWriteStream('./'+ filename.filename))
    res.send(filename)
  } catch (error) {
    res.status(400).json({error: "File not found, file ID does not exist"})
  }
}

The loop I do to find the filename is not optimal and very slow, but I haven't found another way to access the filename field of fs.files. Doing bucket.find({_id: id}) doesn't seem to actually find the file, it returns nothing (I don't think find() takes any arguments). However, the documentations state that there should be a way to get the filename field from the fs.files collection, but doesn't explain how to do it. I'm pretty stuck on this...

Otherwise, I also tried directly querying from the fs.files collection, but since I have no model for that collection, I don't have a model name to do the query on. Most sources use a syntax similar to `db.fs.files.findOne({_id: id}), but I get an error saying that db is not defined. This issue seems easy to solve, but I've been stuck for days. Any help would be so appreciated.


Solution

  • I have solved my problem! The answer was pretty simple, as expected:

    const files = conn.collection('fs.files')
    const filename = await files.findOne({_id: mongooseID}, {projection:{filename : 1, _id : 0}})