下载前必须校验chunk数量是否匹配,否则会因files与chunks数量不一致导致“chunk not found”异常;ID类型不一致、副本集延迟、修复操作无效等问题均需针对性处理。

下载前必须校验 chunk 数量是否匹配
“chunk not found”不是网络抖动或权限问题,而是 files 集合里记录的块数与 chunks 集合中实际文档数不一致。驱动在拼装文件时按序查 n 字段,一旦缺一个就直接抛异常,不会重试或降级。
常见错误是跳过校验、直接调 openDownloadStream。它默认不检查完整性,只管读——结果就是崩溃在下游服务里,日志里只看到未捕获的异常。
- 先查
files文档:db.fs.files.findOne({ _id: ObjectId("...") }),拿到length和chunkSize - 算理论 chunk 数:
Math.ceil(length / chunkSize) - 再查真实数量:
db.fs.chunks.countDocuments({ files_id: ObjectId("...") }) - 两者不等 → 拒绝下载,记录告警,不要继续走流式读取逻辑
ID 类型不一致会导致 openDownloadStream 返回 null
这不会报错,但会静默失败:你传的是字符串 ID(比如 UUID),而 fs.files._id 是 ObjectId,查询无结果,openDownloadStream 直接返回 null,后续调 .pipe() 或 .on('data') 就崩了。
尤其在 PHP 或 Node.js 里混用上传/下载路径时高发——上传用 uploadFromFile 返回的是 ObjectId,但业务层存成字符串没转回,下载时又当字符串传进去。
- 上传后立刻验证返回值类型:
typeof fileId === 'object' && fileId?._bsontype === 'ObjectId' - 存储前统一转成字符串?不行——下载时得再转回来;稳妥做法是全程保持
ObjectId实例 - 在下载入口加类型断言:
if (!fileId || !(fileId instanceof ObjectId)) throw new Error('invalid file_id')
副本集延迟节点可能让 chunk 查询“消失”
默认 readPreference: 'primary',但如果主节点刚完成写入、从节点还没同步完 chunk,而你的请求路由到了那个延迟节点,chunks 查询就查不到最新块,触发 “chunk not found”。
这不是数据损坏,是读视角不一致。靠重试解决不了,因为下次还可能落到同一个延迟节点上。
- 显式设
readPreference: 'primaryPreferred',优先读主,主不可用才切从 - 给下载流加超时:
stream.setTimeout(30000),避免挂起阻塞整个连接池 - 不要对单次下载做无限重试——chunk 缺失是持久性错误,应降级为 404 或触发后台修复任务
修复已损坏文件不能靠 repairDatabase
db.repairDatabase() 对 GridFS 无效。它只修底层存储文件(如 .wt 文件损坏),不校验 fs.files 和 fs.chunks 的逻辑一致性。就算返回 {"ok": 1},缺 chunk 还是缺。
真正能做的只有两件事:一是从备份恢复整个 fs.chunks 集合;二是抢救性重建元数据——用残留的 chunk 反推 files_id、总大小、最大 n,再补一条 fs.files 记录(注意 uploadDate 必须是 Date 类型,filename 不可为空)。
最常被忽略的一点:修复后必须重新校验 chunk 数量,否则下次下载还会崩。校验不是可选项,是每次下载前的强制步骤。

















