概述
本指南介绍如何使用 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' }
}));
查询文件信息
文件元数据位于桶的 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。
其他资源
原题: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 背书,许可免责声明适用。当前没有发布,网站用途是否符合非商业条件尚未核实。未执行上传、下载、重命名或删除操作。











暂无评论内容