GridFS本身不支持分片上传,必须由客户端实现拆分、顺序写chunk、幂等校验及最终提交fs.files元数据;uploadFromStream是单次流式接口,无法应对前端分片上传的中断、重试和乱序场景。

GridFS 本身不支持分片上传,所有“分片上传”逻辑必须由客户端实现。服务端只负责写入 fs.chunks 和 fs.files 文档,不校验顺序、不保障原子性、也不提供断点续传能力。
为什么不能直接用 uploadFromStream 做分片上传
uploadFromStream 是单次流式上传接口,内部会自动切 chunk、递增 n 字段、最后写 fs.files。它假设整个流是连续、可靠、不可中断的——这和前端分片上传(如用户暂停、网络抖动、页面刷新)完全冲突。
常见错误现象包括:
- 调用多次
uploadFromStream同一个文件名 → 生成多个独立文件(fs.files多条记录),而非一个文件的多个分片 - 用不同
_id分别上传 →fs.chunks中的files_id不一致,驱动无法拼合 - 提前插入
fs.files→ 驱动可能在 chunk 未写完时就返回“文件存在”,但读取时报错或内容截断
如何安全生成并写入 chunk
关键不是“并发快”,而是“顺序准、可重试、可验证”。必须由客户端严格控制 n 和 files_id:
-
n必须用Math.floor(offset / chunkSize)计算,不能靠计数器——避免多线程/重试导致序号错位 - 所有 chunk 的
files_id必须是同一个唯一 ID(如 UUID),且该 ID 同时作为最终fs.files._id - 每个 chunk 写入前,先查
fs.chunks.findOne({ files_id: uploadId, n: targetN });存在则跳过(幂等) - 必须串行写入或用
insertOne+ 显式错误处理;避免insertMany导致网络层乱序
怎么判断上传真正完成
不能依赖 fs.files 是否存在,也不能只看是否“没报错”。正确做法是三重校验:
- 写完全部 chunk 后,再插入
fs.files文档,且必须包含length、chunkSize、uploadDate字段 - 执行
fs.chunks.countDocuments({ files_id: uploadId }),确认数量等于Math.ceil(fileSize / chunkSize) - 读取该
uploadId对应的文件,用bucket.openDownloadStreamById(uploadId)尝试流式读取前 1KB —— 若成功且长度匹配,才认为可用
容易被忽略的坑
最常被绕过的其实是状态持久化:上传中途失败后,客户端不知道哪些 chunk 已写、哪些已跳过。MongoDB 不提供跨集合事务回滚,所以你必须把 uploadId、当前已上传 n 列表、总 chunk 数存到本地(如 IndexedDB 或 localStorage),否则刷新页面就只能重头传。另外,fs.chunks 没有 TTL 索引,废弃的上传碎片不会自动清理,得配定时任务或手动回收。


















