网络中断后,GridFS会留下未完成上传的残留数据:fs.chunks中存有已写入的部分块,fs.files中可能已有length为0、uploadDate为空或status为"uploading"的异常文件记录;需先查出此类文件,再按files_id精准删除对应chunks和files文档,严禁使用bucket.drop()。

网络中断后,GridFS 会留下哪些残留数据?
中断发生时,fs.chunks 中可能已写入部分块文档,而 fs.files 中可能已有元数据记录(取决于是否调用了 openUploadStream() 并触发了文件文档插入),但该文档的 length 字段为 0 或未完整,uploadDate 可能为空,且 metadata.status 若自定义了状态字段,大概率停留在 "uploading"。
这些“半成品”不会被自动清理 —— GridFS 本身不提供上传生命周期管理,它只管存取,不管业务语义。
如何安全清理未完成的上传分块?
不能直接删 fs.chunks 文档,必须和 fs.files 关联清理,否则会破坏 GridFS 内部一致性。正确做法是:先查出疑似中断的文件记录,再用其 _id 删除对应的所有块。
- 用
find()定位异常文件:比如status: "uploading"且uploadDate: null,或length: 0,或uploadDate超过 24 小时(按业务容忍窗口设) - 对每个匹配的
file._id,执行fs.chunks.deleteMany({ files_id: file._id }) - 最后删除
fs.files中该条记录:fs.files.deleteOne({ _id: file._id })
注意:MongoDB 不支持跨集合事务(GridFS 不在事务范围内),所以删除顺序必须是 chunks → files,避免出现孤立块。
为什么不能依赖 bucket.drop() 或清空整个 bucket?
bucket.drop() 会删掉整个 fs.files 和 fs.chunks 集合 —— 这等于清空所有已上传文件,不是“清理未完成项”,而是灾难性误操作。
生产环境严禁使用。哪怕你只打算清理一个失败上传,也必须基于 files_id 精确过滤,而不是靠集合名或前缀批量操作。
客户端重试时,如何避免重复上传已存在的块?
GridFS 本身不提供块级去重,需自行实现校验逻辑:
- 上传前,用文件 ID + 块索引(如
n=3)生成唯一 key,例如"{fileId}_chunk_3" - 服务端检查该 key 是否已在 Redis 或数据库中标记为“已接收”
- 若已存在,跳过上传,直接返回确认;否则走正常
bucket.openUploadStream()流程
关键点:这个校验必须在写入 fs.chunks 前完成,且需原子性(建议用 Redis 的 SETNX 或 MongoDB 的 findOneAndUpdate 做幂等标记)。
断点续传真正的难点不在“怎么传”,而在“怎么判断哪些没传、哪些传了一半、哪些传完了但没合并”。GridFS 只提供存储基座,所有状态追踪、超时判定、清理边界都得由业务代码兜底。











