node.js中使用mongodb驱动v4+必须用gridfsbucket替代已移除的grid类,上传需通过openuploadstream传readable流并保存返回的objectid作为文件id,下载须用opendownloadstream配合objectid且主动pipe或监听事件。

GridFS在MongoDB驱动v4+中已移除Grid类,必须用GridFSBucket
Node.js官方MongoDB驱动从v4.0起彻底删除了旧版Grid构造函数,直接调用new Grid(...)会报ReferenceError: Grid is not defined。这不是配置或连接问题,是API断层——必须改用GridFSBucket。
常见错误现象:照着老教程写const grid = new Grid(db, 'fs'),运行就崩;或者误以为gridfs-stream还能用,结果它底层仍依赖已废弃的Grid,npm install后实际无法工作。
- 新版必须通过
mongodb.MongoClient连接后,用client.db().collection('fs.files')或更推荐的new GridFSBucket(db, { bucketName: 'fs' })初始化 -
bucketName默认是'fs',对应fs.files和fs.chunks两个集合,别名不匹配会导致文件写入但查不到 - 不要混用
gridfs-stream和原生GridFSBucket——前者已停止维护,且与v4+驱动不兼容
上传文件必须手动处理流(Readable),不能直接传Buffer或路径字符串
GridFSBucket的openUploadStream只接受Readable流,传fs.readFileSync()返回的Buffer会报TypeError: source.on is not a function;传./avatar.png这种路径字符串则根本不会触发写入。
典型使用场景:Express接收multipart/form-data上传时,req.file.buffer是Buffer,需转为流;读本地文件时要用fs.createReadStream()而非fs.readFile()。
- 转
Buffer为流:const stream = Readable.from(buffer)(Node.js ≥12.3.0) - Express + Multer场景:
const uploadStream = bucket.openUploadStream(filename); req.file.stream.pipe(uploadStream),注意req.file.stream是Multer注入的Readable - 可选传
metadata对象,比如{ userId: 'abc123', contentType: 'image/png' },后续查询能用上
下载文件要主动监听data事件或用pipe,否则openDownloadStream返回的流不会自动消费
bucket.openDownloadStream(fileId)返回一个Readable流,但如果不监听data、end事件,或不pipe到响应体,流会挂起,HTTP请求永远不结束,客户端卡住。
ZenHeart zenlink 与 zenlink-mcp 为 OpenClaw,涵盖安装、MCP 注册、钩子接线、入站轮询及升级维护。
容易踩的坑:写const stream = bucket.openDownloadStream(fileId); stream.on('data', ...)却忘了stream.on('end', () => res.end()),导致部分文件下载后连接未关闭;或在Express里漏设res.setHeader('Content-Type', ...),浏览器无法正确解析文件类型。
- 安全做法:
stream.pipe(res),Express会自动处理end和错误传播 - 若需修改文件名,加
res.setHeader('Content-Disposition', 'attachment; filename="report.pdf"') - 查不到文件时
openDownloadStream返回null,不是抛错,得先if (!stream) { res.status(404).end(); return; }
文件ID用ObjectId还是字符串?上传后务必保存返回的_id字段
上传成功后uploadStream的promise resolve 返回的是一个{ _id: ObjectId(...) }对象,这个_id才是后续下载/删除的唯一凭证。有人误用原始文件名当ID,结果同名文件覆盖或查不到。
ObjectId本质是12字节二进制,字符串形式如"65a7b8c9d0e1f2a3b4c5d6e7",MongoDB shell和大部分ORM都支持直接用字符串查,但Node驱动里传字符串ID时,必须显式转成ObjectId实例,否则find({ _id: 'xxx' })查不到。
- 上传后立刻存
result._id到业务表,别存filename或md5 - 下载时:
bucket.openDownloadStream(new ObjectId(fileIdStr)),漏掉new ObjectId(...)就会静默失败 - 删除用
bucket.delete(new ObjectId(fileIdStr)),同样需要转换
GridFS本身不校验文件完整性,大文件上传中断时可能残留fs.chunks碎片,生产环境建议加MD5校验或用uploadStream.on('error', ...)兜底清理。










