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.files 加 expireAt 字段 + 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. 性能调优
| 参数 | 建议 |
|---|---|
chunkSize | 256 KB ~ 1 MB(太大浪费内存,太小索引开销大) |
| 索引 | fs.files.filename(频繁按名查询)fs.chunks.files_id + n(复合索引已默认) |
| 存储引擎 | WiredTiger(压缩 + 高效) |
| 读写分离 | 使用 Replica Set,读请求指向 Secondary |
| 批量上传 | 使用 GridFSBucket.openUploadStream + 流式写入 |
6. 与对象存储对比(S3 / MinIO)
| 维度 | GridFS | S3/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,欢迎继续提问!