使用 GridFS 存储大文件

概述

本指南介绍如何使用 GridFS 在 MongoDB 中存储和读取大文件。GridFS 规范规定了存储时如何把文件拆成块,以及读取时如何重新组装。驱动的 GridFS 实现负责文件存储的组织和操作。

文件大小超过 BSON 单文档的 16 MB 限制时,可以使用 GridFS。是否适合具体场景,详见服务端 GridFS 手册。

下文依次介绍桶的创建、上传、元数据查询、下载、重命名、删除文件及删除整个桶。

GridFS 如何工作

GridFS 用桶(bucket)组织文件。桶由一组 MongoDB 集合组成,保存文件块及描述信息,按规范使用以下集合:

  • chunks 保存二进制文件块。
  • files 保存文件元数据。

创建桶时,除非指定其他名称,驱动使用默认桶名 fs 作为两个集合的前缀。驱动还为各集合创建索引,以高效读取文件及相关元数据。如果桶尚不存在,驱动会在第一次写入时创建;索引只在尚不存在且桶为空时创建。详见 GridFS 索引。

存储文件时,驱动将其拆成较小部分,每部分对应 chunks 集合中的一个文档;同时在 files 中创建文档,记录唯一文件 ID、文件名和其他元数据。上传来源可以是内存或流。官方原文的文件分块示意图路径是 /includes/figures/GridFS-upload.png;此处仅记录配图位置,未下载或视觉核验该图。

读取时,GridFS 从指定桶的 files 中取得元数据,据此从 chunks 文档重组文件。可以读入内存,也可以输出为流。

创建 GridFS 桶

先创建桶或取得已有桶的引用。将数据库作为参数创建 GridFSBucket,再通过该实例读写桶内文件:

const db = client.db(dbName);
const bucket = new mongodb.GridFSBucket(db);

原文将自定义桶名操作描述为传给 create() 的第二个参数;其实际示例使用下面的构造函数选项,代码按原文保留:

const bucket = new mongodb.GridFSBucket(db, { bucketName: 'myCustomBucket' });

详见 GridFSBucket API。

上传文件

使用桶的 openUploadStream() 为指定文件名创建上传流,再用 pipe() 把 Node.js 读取流连接到上传流。该方法支持文件块大小和额外元数据字段等选项。下例中的 fs 表示 Node.js 文件系统模块:

fs.createReadStream('./myFile').
     pipe(bucket.openUploadStream('myFile', {
         chunkSizeBytes: 1048576,
         metadata: { field: 'myField', value: 'myValue' }
     }));

详见 openUploadStream API。

查询文件信息

文件元数据位于桶的 files 集合,包括:

  • 文件 _id
  • 文件名
  • 文件长度或大小
  • 上传日期和时间
  • 可存储额外信息的 metadata 文档

调用桶的 find() 查询文件,返回 FindCursor,可从中读取结果。下例用 for await...of 遍历所有文件元数据并输出:

const cursor = bucket.find({});
for await (const doc of cursor) {
   console.log(doc);
}

find() 接受不同查询条件,也可组合 sort()、limit() 和 project()。

相关资料:

下载文件

使用 openDownloadStreamByName() 创建下载流。下例根据存储在 filename 字段中的名称,把文件下载到工作目录:

bucket.openDownloadStreamByName('myFile').
     pipe(fs.createWriteStream('./outputFile'));

如果多个文档的 filename 相同,GridFS 会根据 uploadDate 选择该名称最近上传的文件。

也可以使用 openDownloadStream(),按文件 _id 下载:

bucket.openDownloadStream(ObjectId("60edece5e06275bf0463aaf3")).
     pipe(fs.createWriteStream('./outputFile'));

GridFS 流 API 无法只加载一个块的一部分。下载流需要从 MongoDB 拉取块时,会把整个块读进内存。默认 255 KB 的块通常足够,也可减小块大小以降低内存开销。详见 openDownloadStreamByName API。

重命名文件

使用 rename() 更新文件名,必须通过 _id 指定文件,不能以名称指定。

该方法每次只更新一个文件。要批量重命名,应先查询匹配文件,提取所需文件 ID,再分别调用 rename。下例通过 ID 把 filename 改为 newFileName:

bucket.rename(ObjectId("60edece5e06275bf0463aaf3"), "newFileName");

详见 rename API。

删除文件

使用 delete() 删除文件,必须按 _id 指定,而不是名称。每次调用只删除一个文件;批量删除时,先查询文件、取得各 ID,再分别调用:

bucket.delete(ObjectId("60edece5e06275bf0463aaf3"));

详见 delete API。

删除整个 GridFS 桶

drop() 删除桶的 files 和 chunks 集合,等效于删除整个桶:

bucket.drop();

详见 drop API。

其他资源

MongoDB GridFS 规范


原题:Store Large Files with GridFS。作者:MongoDB Docs Team / MongoDB, Inc.。官方原文;对应文档源码。

此文档子项目的 README.rst 明确规定全部文档使用 CC BY-NC-SA 3.0。本稿为非商业离线中文翻译,进行了语言和排版转换,对原文 create() 文字与构造函数代码的不一致作了明确标记,保留全部九个代码块。本译稿同样按 CC BY-NC-SA 3.0 提供;不代表 MongoDB 背书,许可免责声明适用。当前没有发布,网站用途是否符合非商业条件尚未核实。未执行上传、下载、重命名或删除操作。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容