从MongoDB GridFS流式传输文件

不久前,我在Twitter上发布了自己的最新作品,即从MongoDB GridFS传输文件进行下载(而不是将整个文件存储到内存中然后提供服务),这是我取得的一个小胜利。 我答应就此事写博客,但不幸的是,我的特定用法与我的项目的领域有点相关,所以我不能仅仅展示它。 因此,我整理了一个示例node.js + GridFS应用程序 ,并在github上进行了共享,并将使用本文来解释我是如何实现的。 :)

GridFS模块

首先,特殊道具去tjholowaychuk谁在#node.js的IRC频道作出回应时,我问,如果任何人有运气使用GridFS的从猫鼬 。 我得到的很多代码都来自他与我分享的要旨。 无论如何,到代码。 我将描述如何使用gridfs,并在完成基础工作后说明从GridFS流式传输文件的过程是如此简单。

我创建了一个gridfs模块,该模块基本上通过mongoose(我在整个应用程序中使用)访问GridStore,该模块还可以共享将mongoose连接到mongodb服务器时创建的数据库连接。

mongoose = require "mongoose"
request  = require "request"

GridStore = mongoose.mongo.GridStore
Grid      = mongoose.mongo.Grid
ObjectID = mongoose.mongo.BSONPure.ObjectID

如果我们不能在mongodb中添加任何文件,我们将无法获取文件,因此让我们创建一个putFile操作。

exports.putFile = (path, name, options..., fn) ->
  db = mongoose.connection.db
  options = parse(options)
  options.metadata.filename = name
  new GridStore(db, name, "w", options).open (err, file) ->
    return fn(err)  if err
    file.writeFile path, fn



parse = (options) ->
  opts = {}
  if options.length > 0
    opts = options[0]
  if !opts.metadata
    opts.metadata = {}
  opts

实际上,这只是委托给GridStore中存在的putFile操作(作为mongodb模块的一部分)。 我也有一些逻辑来解析选项,如果没有提供默认值,则提供默认值。 要注意的一个有趣功能是,我将文件名存储在元数据中,因为当时我遇到了一个有趣的问题,即从gridFS检索的文件将id作为文件名(即使在mongo中查看发现文件名实际上是在数据库)。

现在进行get操作。 此方法的原始实现只是通过调用store.readBuffer()将内容作为缓冲区传递给所提供的回调,但是现在已更改为将结果存储对象传递给回调。 其值是调用者可以使用商店对象来访问元数据,contentType和其他详细信息。 用户还可以确定他们想如何读取文件(进入内存还是使用ReadableStream)。

exports.get = (id, fn) ->
  db = mongoose.connection.db
  id = new ObjectID(id)
  store = new GridStore(db, id, "r",
    root: "fs"
  )
  store.open (err, store) ->
    return fn(err)  if err
    # band-aid
    if "#{store.filename}" == "#{store.fileId}" and store.metadata and store.metadata.filename
      store.filename = store.metadata.filename
    fn null, store

这段代码有一个小问题,它检查文件名和fileId是否相等。 如果是的话,它将检查是否设置了meta.filename并将store.filename设置为在那里找到的值。 我已经提出了这个问题,以后再进行调查。 :)

该模型

在我的特定实例中,我想将文件附加到模型。 在此示例中,我们假设我们有一个可以附加任意数量文件的应用程序(作业,贷款应用程序等)。 想想税收收据,完整的申请表以及其他扫描文件。

ApplicationSchema = new mongoose.Schema(
  name: String
  files: [ mongoose.Schema.Mixed ]
)
ApplicationSchema.methods.addFile = (file, options, fn) ->
  gridfs.putFile file.path, file.filename, options, (err, result) =>
    @files.push result
    @save fn

在这里,我将文件定义为混合对象类型的数组(意味着它们可以是任何东西)和方法addFile,该方法基本上采用一个至少包含路径和文件名属性的对象。 它使用它来将文件保存到gridfs并将结果的gridstore文件对象存储在files数组中(其中包含诸如id,uploadDate,contentType,名称,大小等之类的东西)。

处理要求

所有这些都插入到请求处理程序中,以处理向/ new提交的表单。 所有这一切都需要创建一个Application模型实例,从请求中添加上载的文件(在本例中,我们将文件字段命名为“ file”, 因此命名为req.files.file )并保存它。

app.post "/new", (req, res) ->
  application = new Application()
  application.name = req.body.name
  opts = 
    content_type: req.files.file.type
  application.addFile req.files.file, opts, (err, result) ->
    res.redirect "/"

现在,所有这些工作的总和使我们可以非常轻松地从gridFS下载请求的文件,从而获得丰厚的回报。

app.get "/file/:id", (req, res) ->
  gridfs.get req.params.id, (err, file) ->
    res.header "Content-Type", file.type
    res.header "Content-Disposition", "attachment; filename=#{file.filename}"
    file.stream(true).pipe(res)

在这里,我们只是通过id查找文件,并使用生成的文件对象来设置Content-Type和Content-Disposition字段,最后使用ReadableStream :: pipe将文件写出到响应对象(这是WritableStream的实例) )。 这是将数据从MongoDB流传输到客户端的魔力。

主意

这只是一个卑微的开始。 其他想法包括将gridfs完全封装在模型中。 更进一步,我们甚至可以将gridfs模型变成猫鼬插件,以允许完全黑盒使用gridfs。

随时检查该项目 ,让我知道您是否有进一步的想法。 叉开! :)

参考: 敏捷开发人员博客的Rant and Musings中我们的JCG合作伙伴 James Carr 从MongoDB GridFS流式传输文件


翻译自: https://www.javacodegeeks.com/2012/01/streaming-files-from-mongodb-gridfs.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值