MongoDB GridFS

MongoDB GridFS 详解

GridFS 是 MongoDB 提供的 分布式文件存储规范,用于将 超过 16 MB(BSON 文档上限)的文件拆分成 chunks(默认 255 KB)存储到两个集合中:

集合作用
fs.files存储文件的 元数据(文件名、长度、上传时间、MD5、自定义 metadata 等)
fs.chunks存储文件的 二进制分片(每条记录含 files_id + n + data

1. 为什么需要 GridFS?

场景说明
大文件超过 16 MB 的图片、视频、日志、备份等
流式读写支持 Range 请求(断点续传、视频分段播放)
分布式与 Replica Set / Sharded Cluster 天生兼容
统一管理文件与业务数据同库,便于备份、复制、权限控制

2. 核心概念

+--------------------+       +---------------------+
|   fs.files         |       |   fs.chunks         |
+--------------------+       +---------------------+
| _id (ObjectId)     |<--+   | _id (ObjectId)      |
| filename           |   |   | files_id (ref)      |
| length             |   |   | n (chunk 序号)      |
| chunkSize          |   |   | data (Binary)       |
| uploadDate         |   +---|---------------------|
| md5                |
| metadata           |
+--------------------+
  • chunkSize:默认 255 KB(可自定义,但所有 chunk 必须同大小,最后一块可小于)。
  • files_id:关联 fs.files._id,MongoDB 自动维护。
  • MD5:用于校验文件完整性(可选)。

3. 常用操作(驱动示例)

3.1 Node.js(mongodb 驱动 v6+)

const { MongoClient, GridFSBucket } = require('mongodb');
const fs = require('fs');

async function run() {
  const client = new MongoClient('mongodb://localhost:27017');
  await client.connect();
  const db = client.db('mydb');

  // 1. 上传文件
  const bucket = new GridFSBucket(db, { bucketName: 'fs' });
  const readStream = fs.createReadStream('./bigvideo.mp4');
  const uploadStream = bucket.openUploadStream('bigvideo.mp4', {
    chunkSizeBytes: 1024 * 256,           // 256 KB
    metadata: { type: 'video/mp4', tag: 'demo' }
  });
  readStream.pipe(uploadStream);
  const fileId = await new Promise((resolve, reject) => {
    uploadStream.on('finish', () => resolve(uploadStream.id));
    uploadStream.on('error', reject);
  });
  console.log('上传完成,fileId:', fileId);

  // 2. 下载文件(支持 Range)
  const downloadStream = bucket.openDownloadStream(fileId);
  downloadStream.pipe(fs.createWriteStream('./downloaded.mp4'));

  // 3. 按范围下载(视频切片)
  const rangeStream = bucket.openDownloadStream(fileId, {
    start: 1024 * 1024,   // 从 1MB 开始
    end: 2 * 1024 * 1024   // 到 2MB 结束
  });
  rangeStream.pipe(fs.createWriteStream('./slice.mp4'));

  await client.close();
}
run().catch(console.dir);

3.2 Python(pymongo

from pymongo import MongoClient
from gridfs import GridFS
import bson

client = MongoClient('mongodb://localhost:27017')
db = client.mydb
fs = GridFS(db)

# 上传
with open('photo.jpg', 'rb') as f:
    file_id = fs.put(f, filename='photo.jpg', content_type='image/jpeg', tags=['avatar'])

# 下载
grid_out = fs.get(file_id)
with open('photo_copy.jpg', 'wb') as f:
    f.write(grid_out.read())

# 流式下载(Range)
grid_out = fs.get(file_id)
grid_out.seek(1024*100)          # 跳到 100KB
chunk = grid_out.read(1024*50)   # 读取 50KB

3.3 Shell(mongosh

// 上传(需先用驱动或 mongofiles)
mongofiles -d mydb put large.iso

// 查询文件元数据
db.fs.files.find().pretty()

// 查询前 10 条 chunk
db.fs.chunks.find({files_id: ObjectId("...")}).limit(10)

工具mongofiles(MongoDB 官方 CLI,已废弃于新版),推荐使用驱动或 MongoDB Compass


4. 高级功能

功能实现方式
断点续传客户端记录已上传 n,继续 openUploadStreamWithId
分片集群GridFS 自动跨 shard 分布 fs.chunks
TTL 自动清理fs.filesexpireAt 字段 + TTL 索引
版本控制同一 filename 多次 put,保留不同 _id
CDN 加速配合 GridFS + Nginx X-Accel-Redirect

示例:TTL 自动删除 7 天前的文件

// 在上传时加入 expireAt
fs.put(data, {
  filename: 'temp.log',
  metadata: { expireAt: new Date(Date.now() + 7*24*60*60*1000) }
});

// 创建 TTL 索引
db.fs.files.createIndex({ "metadata.expireAt": 1 }, { expireAfterSeconds: 0 });

5. 性能调优

参数建议
chunkSize256 KB ~ 1 MB(太大浪费内存,太小索引开销大)
索引fs.files.filename(频繁按名查询)
fs.chunks.files_id + n(复合索引已默认)
存储引擎WiredTiger(压缩 + 高效)
读写分离使用 Replica Set,读请求指向 Secondary
批量上传使用 GridFSBucket.openUploadStream + 流式写入

6. 与对象存储对比(S3 / MinIO)

维度GridFSS3/MinIO
部署与 DB 同节点独立服务
事务支持(同一 DB)不支持
成本磁盘 = DB按量计费
延迟低(局域网)网络依赖
元数据查询强大(MongoDB 查询)有限(需额外 DB)
适用中等规模、强关联海量冷存储、CDN

推荐热数据 + 强查询 用 GridFS;冷归档 + 高并发 用 S3。


7. 常见问题(FAQ)

问题解答
文件损坏如何检测?比较 fs.files.md5 与重新计算的 MD5
能否直接修改 chunk?不推荐,手动改 fs.chunks 易导致不一致
GridFS 支持事务吗?MongoDB 4.0+ 单文档事务 不适用;多文档事务 可包裹 fs.files + fs.chunks(需 Replica Set)
如何备份?mongodump 自动包含 fs.* 集合;或使用 mongodump --db=mydb --collection=fs.files

8. 快速上手(Docker 一键部署)

docker run -d --name mongo-gridfs \
  -p 27017:27017 \
  -v mongo_data:/data/db \
  mongo:7 --replSet rs0

# 初始化副本集
docker exec -it mongo-gridfs mongosh --eval 'rs.initiate()'

然后使用任意语言驱动按上述代码操作即可。


参考资源

  • 官方文档:https://www.mongodb.com/docs/manual/core/gridfs/
  • GitHub 示例:https://github.com/mongodb/mongo-gridfs-examples
  • mongofiles(旧)→ 推荐 Compass驱动

一句话总结

GridFS = 大文件 + MongoDB 查询 + 流式读写,适合需要 统一存储、强元数据、事务关联 的场景。

如需 完整代码模板、性能测试脚本、或与 RockMongo 集成查看 GridFS,欢迎继续提问!

文章已创建 2481

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部