Skip to content

MongoDB - GridFS

GridFS 是 MongoDB 中用于存储和检索超出 BSON 文档大小限制(16 MB)文件的规范。GridFS 不会将文件存储在单个文档中,而是将文件分割成更小的部分,即分块(chunks),并将每个分块作为单独的文档存储。这允许将视频、高分辨率图像或大量日志文件等大型文件直接存储在 MongoDB 数据库中。

默认情况下,GridFS 使用两个集合来管理文件:fs.files 和 fs.chunks。

  • fs.files:此集合存储文件的元数据(metadata)。此集合中的每个文档代表一个文件,并包含 filename(文件名)、chunkSize(分块大小)、uploadDate(上传日期)以及自定义元数据等信息。
  • fs.chunks:此集合存储文件的二进制分块。每个分块通过 files_id 字段链接回其父文件,该字段对应于 fs.files 集合中文档的 _id。

此文档包含有关所存储文件的高级信息。

{
"_id": ObjectId("64f7c2f1b3e9a1e8a8b1a8d5"),
"length": 10485760, // 文件的总大小(字节)
"chunkSize": 261120, // 每个分块的大小(字节,默认 255k)
"uploadDate": ISODate("2023-09-06T00:00:00.000Z"),
"filename": "large_video.mp4",
"contentType": "video/mp4", // 可选:用户自定义元数据
"metadata": { "author": "John Doe" } // 可选:用户自定义元数据
}

此文档包含实际文件数据的一部分。

{
"_id": ObjectId("64f7c300b3e9a1e8a8b1a8d6"),
"files_id": ObjectId("64f7c2f1b3e9a1e8a8b1a8d5"), // 链接到 fs.files 文档
"n": 0, // 分块的序列号(基于 0)
"data": BinData(0, "...binary data of the chunk...") // 实际数据
}

虽然 mongofiles 命令行工具可用于手动操作,但大多数现代应用程序通过 MongoDB 驱动程序以编程方式与 GridFS 交互。以下示例演示了如何使用官方 Node.js 驱动程序上传和下载文件。

首先,确保您已在项目中安装 Node.js 驱动程序:

npm install mongodb

此代码片段展示了如何连接到数据库,将文件从本地文件系统上传到 GridFS,然后将其下载回来。

const { MongoClient } = require('mongodb');
const fs = require('fs');
const path = require('path');
async function main() {
const uri = 'mongodb://localhost:27017';
const client = new MongoClient(uri);
try {
await client.connect();
const database = client.db('myGridFSDB');
const bucket = new mongodb.GridFSBucket(database);
const filename = 'example.txt';
const filepath = path.join(__dirname, filename);
// 创建一个示例文件以上传
fs.writeFileSync(filepath, 'Hello, GridFS!');
// 1. 将文件上传到 GridFS
const uploadStream = bucket.openUploadStream(filename, {
chunkSizeBytes: 1024, // 自定义分块大小
metadata: { owner: 'system' }
});
const readStream = fs.createReadStream(filepath);
readStream.pipe(uploadStream);
console.log(`文件 '${filename}' 上传成功。`);
// 2. 从 GridFS 下载文件
const downloadStream = bucket.openDownloadStreamByName(filename);
const downloadedFilepath = path.join(__dirname, 'downloaded_example.txt');
const writeStream = fs.createWriteStream(downloadedFilepath);
downloadStream.pipe(writeStream);
writeStream.on('finish', () => {
console.log(`文件 '${filename}' 下载成功。`);
client.close();
});
} catch (e) {
console.error('发生错误:', e);
await client.close();
}
}
main().catch(console.error);
  • 当您需要存储大于 16 MB 的文件时。
  • 当您希望将文件及其元数据一起存储在 MongoDB 中,而不是使用单独的文件存储系统(如 AWS S3 或本地文件系统)时。
  • 当您想访问文件的部分内容而无需将整个文件加载到内存中时(例如,流式传输视频或查找大型日志文件的特定部分)。

对于许多用例,特别是在云原生应用程序中,将大文件存储在专用的对象存储服务(如 Amazon S3 或 Google Cloud Storage)中可能更具成本效益和可扩展性。在这种情况下,您将在 MongoDB 文档中存储指向该对象的 URL 或标识符,而不是文件本身。