如何通过其 id 获取 MongoDB GridFS 文件的文件名?

huangapple go评论62阅读模式
英文:

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

问题

如果我知道存储在GridFS中的文件的Mongo ID,并且想要根据其fs.files文件名字段中存储的文件名下载它,但没有模型来调用集合,所以我不知道该如何对该集合进行查询。

这是我的server.js文件,其中我连接到数据库:

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

// 连接到mongoose
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true })
    .then(() => {
        // 监听请求
        app.listen(process.env.PORT, () => {
            console.log(`Connected to MongoDB and listening on port`, process.env.PORT)
        })
        
    })
    .catch((error) => {
        // 如果MONGO_URI字符串具有错误的用户或密码,则引发错误
        console.log(error)
})

在我的files controllers文件中,我有这个:

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

// 创建用于存储文件的存储桶
// 存储桶初始化为fs,并有2个集合:fs.files和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)

  // 获取文件名
  const cursor = bucket.find({}) // find()返回一个FindCursor可迭代对象
  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"})
  }
}

我用来查找文件名的循环不是最佳的,而且非常慢,但我没有找到另一种访问fs.files的filename字段的方法。尝试使用 bucket.find({_id: id}) 似乎实际上找不到文件,它什么也不返回(我认为find()不接受任何参数)。然而,文档中指出应该有一种方法可以从fs.files集合中获取filename字段,但没有解释如何做到这一点。我对此很困惑...

此外,我还尝试直接从fs.files集合中查询,但由于我没有该集合的模型,所以没有模型名称来执行查询。大多数来源使用类似于 db.fs.files.findOne({_id: id}) 的语法,但我收到一个错误,说db未定义。这个问题似乎很容易解决,但我已经卡住了好几天了。
任何帮助将不胜感激。

英文:

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.

答案1

得分: 0

我已解决我的问题!答案非常简单,正如我所预期:

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

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}})

huangapple
  • 本文由 发表于 2023年6月15日 02:22:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76476517.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定