open_download_stream 的 start/end 参数是唯一合法的 offset 控制方式,start 表示起始字节(含),end 表示结束字节(不含),驱动自动定位 chunk、跳过前导字节并截断末尾。

open_download_stream 的 start/end 参数才是唯一合法的 offset 控制方式
GridFS 不支持用 skip/limit 模拟字节偏移,因为 fs.chunks 是按文档存储的,skip(10) 跳的是 10 个 chunk 文档,不是 10×255KB 字节。chunk 大小不均(最后一块可能只有几字节),n 字段也不严格保序(并发上传时可能有空洞或重复),强行跳会直接破坏文件结构。
所有官方驱动都只通过 open_download_stream 提供字节级随机读取能力,它内部自动完成:定位目标 chunk、跳过前导字节、截断末尾溢出部分、校验 chunk 完整性。你只需传两个参数:
-
start:起始字节位置(含),从 0 开始,比如start=1048576表示从第 1MB 开始 -
end:结束字节位置(不含),比如end=1048576+8192表示读取 8KB;若客户端请求Range: bytes=1000-(无 end),则end参数必须省略,否则驱动会读到文件末尾
HTTP Range 请求必须由服务端手动解析并转为 start/end
MongoDB 不解析 HTTP Header,Range: bytes=1000-2000 这类头需要你在 Web 层自己提取并转换。常见错误是没处理边界情况:
- 忽略
Range头直接调用完整下载,导致返回 200 而非 206,浏览器拒绝续传 - 解析出
start=1000、end=2000后,没在响应中写Content-Range: bytes 1000-2000/文件总长度 - 没设响应状态码为
206 Partial Content,浏览器认为这次请求失败,不会发后续 Range - 对
bytes=1000-这种开区间请求,误把end设成极大值(如Math.pow(2, 53)),触发驱动底层异常
上传侧断点续传靠查 fs.chunks.max(n) 确定下一块索引
上传中断后,服务端不能靠 count() 判断已传多少块——因为失败 chunk 可能残留脏数据。正确做法是查该 files_id 下最大的 n 值:
- 执行
db.fs.chunks.find({ files_id: ObjectId("...") }, { n: 1 }).sort({ n: -1 }).limit(1) - 若返回
{ n: 4 },说明 n=0~4 已存在,前端应从 n=5 开始上传新 chunk - 必须用
openUploadStreamWithId(或等价 API),显式传入已有fileId,否则会新建files_id,旧 chunk 成为孤儿 - 并发上传同一文件时,多个请求同时查 max(n) 再上传,可能撞块;需加轻量锁(如 Redis
setnx)或强制前端串行
chunkSize 不一致会导致 open_download_stream 定位失败
open_download_stream 依赖 fs.files.chunkSize 值做 chunk 边界计算。如果上传时自定义了 chunkSizeBytes(如设为 1024×128),但后续下载时驱动读到的 chunkSize 是默认 255×1024,就会错位——比如本该从第 3 个 chunk 的第 100 字节开始,却定位到第 2 个 chunk 尾部。
检查方式:查 fs.files 中对应文件的 chunkSize 字段是否与上传时一致。Laravel-MongoDB 或 Spring Data MongoDB 封装层常默认忽略该参数,需显式透传配置。


















