应直接使用 GridFSBucket,避免gridfs-stream或MongoGridFS;创建时须传Database对象,自定义桶名需配置对象;上传需用流或Buffer,读取须用GridFS接口,删除会清空文件及chunks,索引仅首次写入时自动创建。

直接用 GridFSBucket,别碰老式 gridfs-stream 或已弃用的 MongoGridFS 类——前者维护混乱、API 不一致,后者在新版驱动中彻底移除。
创建 GridFSBucket 实例时必须传入 Database 对象
常见错误是传错参数类型或漏掉数据库实例。PyMongo 和 Node.js 驱动都要求第一个参数是 Database(不是 Client 或连接字符串)。
- Node.js 正确写法:
const bucket = new GridFSBucket(db),其中db是client.db('mydb')返回的对象 - Python(PyMongo)正确写法:
bucket = GridFSBucket(db),db同样是client['mydb'] - 如果传了
client或字符串,会报TypeError: expected Database instance或静默失败 - 指定自定义桶名(如
media)要作为第二个参数:new GridFSBucket(db, { bucketName: 'media' }),注意不是字符串而是配置对象
上传文件必须用流或 Buffer,不能直接传路径字符串
openUploadStream() 只接收文件名和可选配置,不处理磁盘路径;你得自己用 fs.createReadStream() 或 fs.readFileSync() 拉取内容再 pipe 或 write。
- 错误做法:
bucket.openUploadStream('/path/to/file.pdf')—— 这只是把路径当文件名存进files集合,实际没上传任何数据 - 正确做法(流式,推荐大文件):
fs.createReadStream('./local.pdf').pipe(bucket.openUploadStream('report.pdf')) - 正确做法(内存小文件):
await bucket.uploadFromBuffer(buffer, 'avatar.png') - 关键参数:
chunkSizeBytes默认 255 KiB,若频繁读取小范围(如视频跳播),可调小到 64 KiB;但不要低于 16 KiB,否则 chunks 集合文档数暴增,影响查询性能
读取文件时别用 find 直接查 fs.files
手动查 fs.files 只能拿到元数据,没法拼 chunk;必须走 GridFS 提供的读取接口,否则会丢数据或乱序。
- 按文件名读:
const stream = bucket.openDownloadStream({ filename: 'log.tar.gz' })—— 注意是对象,不是字符串 - 按 ID 读(更可靠,尤其重名时):
bucket.openDownloadStreamById(new ObjectId('...')) - 读取后必须 pipe 到目标(如
fs.createWriteStream())或用toArray()收集 buffer,否则流挂起、内存泄漏 - 如果文件不存在,
openDownloadStream不报错,而是返回空流;需监听'error'事件或检查stream.readable状态
删除文件前先确认是否真要删整个文件
delete() 删除的是整个文件(包括所有 chunks + files 记录),不是单个 chunk;且 GridFS 不支持事务,删完无法回滚。
- 安全做法:先用
find({ filename: 'xxx' })查出_id,再调bucket.delete(id) - 批量删?别用
fs.files.remove()—— 它不会清理fs.chunks,导致孤儿块堆积,磁盘持续增长 - 真正需要清理碎片时,用
bucket.drop()清空整个桶(慎用,删光所有文件) - 默认桶名是
fs,但如果你用了自定义名(如media),删的时候也要对应上:new GridFSBucket(db, { bucketName: 'media' }).delete(id)
最容易被忽略的是索引自动创建时机:只有首次写入时才建索引,且仅当集合为空;如果中途手动删过 fs.files 或 fs.chunks,索引可能缺失,导致后续查询极慢——此时得手动在两个集合上补 { files_id: 1, n: 1 } 和 { filename: 1 } 索引。

















