“chunk not found”本质是GridFS数据不一致:files记录的chunk数量与chunks集合实际文档数不符,常见于误删、写入中断或并发冲突;需下载前校验数量匹配,否则拒绝下载并告警。

为什么 chunk not found 不是网络或权限问题,而是数据不一致
这个异常本质是 GridFS 在拼装文件时,发现某个 chunks 集合里的分块缺失——比如 files 集合里记录文件有 12 个 chunk,但实际只查到前 11 个。它和连接超时、认证失败完全不同,不会伴随 connection refused 或 Unauthorized 这类提示,直接报错位置在驱动读取 chunk 时的内部迭代器里。
常见诱因包括:手动删过 chunks 文档、断电/崩溃导致写入中途失败、用非 GridFS 接口(如 collection.insertOne)误操作了 chunks 表、或多个客户端并发写同一个文件没加锁。
下载前必须校验文件完整性的两个动作
别跳过校验直接调 openDownloadStream——这是最常踩的坑。GridFS 驱动默认不校验 chunk 数量是否匹配 files.length 和 files.chunkSize 计算出的理论值。
- 先查
files文档,拿到_id、length、chunkSize和uploadDate - 再用
chunks集合 count 查询:db.fs.chunks.countDocuments({ files_id: ObjectId("...") }),确认结果等于Math.ceil(length / chunkSize) - 如果数量对不上,说明文件已损坏,此时应拒绝下载并告警,而不是让下游触发
chunk not found
示例(Node.js):
const fileDoc = await db.collection('fs.files').findOne({ _id: fileId });<br>const expectedChunks = Math.ceil(fileDoc.length / fileDoc.chunkSize);<br>const actualChunks = await db.collection('fs.chunks').countDocuments({ files_id: fileId });<br>if (actualChunks !== expectedChunks) throw new Error(`chunk mismatch: expected ${expectedChunks}, got ${actualChunks}`);
openDownloadStream 的 timeout 和重试策略要改默认值
驱动默认的 readPreference 是 primary,且无超时控制。当副本集某个节点 chunk 数据滞后(比如刚完成 initial sync),驱动可能连上那个延迟节点,查不到最新写入的 chunk,就直接抛异常,而非自动切到其他节点。
- 显式设置
readPreference: 'primaryPreferred',避免卡死在延迟节点 - 给 stream 加
timeout:例如stream.setTimeout(30000),防止挂起阻塞整个下载流程 - 不要对单次下载做无限重试——chunk 缺失是持久性错误,重试只会重复失败;可降级为返回 404 或触发后台修复任务
修复已损坏文件的唯一安全方式
没有“跳过缺失 chunk 继续下载”的合法方案。GridFS 的 chunk 是按 n 字段严格排序拼接的,缺一块就会导致后续所有字节偏移错乱,解压/播放/解析必然失败。
真正能做的只有两件事:
- 从备份恢复整个
fs.files和fs.chunks对应文档(注意保持 ObjectId 一致) - 如果无备份,且业务允许丢失部分数据,可删掉该文件的
files文档,并清理残留的chunks(用db.fs.chunks.deleteMany({ files_id: ... })),再重新上传
千万别用脚本“伪造”一个空 chunk 补位——n 值错位会让整个文件结构不可逆损坏。
最麻烦的地方在于:这个异常往往在用户点击下载时才暴露,而问题可能发生在几天前的某次异常写入。得靠定期跑校验脚本,而不是等报错再救火。










