gridfs不支持http range请求,需应用层解析range头、查files集合获取chunksize与length、批量查询对应chunks、拼接切片并设置content-range等响应头,返回206状态码;chunksize须每次请求单独查询以防错位。

GridFS本身不支持HTTP Range请求,必须自己实现分片逻辑
GridFS是MongoDB的文件存储规范,底层把大文件拆成多个chunks集合里的文档,每个默认255KB。但它没有内置HTTP服务,也不解析Range头——这意味着直接用mongofiles或驱动读取时,curl -H "Range: bytes=1000-2000"会返回404或完整文件,根本不会切片。
要支持断点续传,你得在应用层拦截HTTP请求、解析Range头、定位对应chunks、拼接二进制流并设置Content-Range响应头。
- 必须先通过
files集合查出目标文件的_id、length、chunkSize(注意:不是所有文件都用默认255KB,可能被自定义) -
chunkSize决定每个chunks文档存多少字节,也决定了如何把字节偏移换算成n(chunk索引)和offset(块内偏移) - 不能只查一个
chunk:跨块的Range(比如从第254KB到256KB)需要读两个chunk文档,并截取首尾
用find({files_id, n: {$in: [...]}})批量读取所需chunk,别用循环逐个查
假设用户请求Range: bytes=500000-999999,文件chunkSize = 255 * 1024 = 261120,那么:
- 起始chunk索引:
Math.floor(500000 / 261120) = 1 - 结束chunk索引:
Math.floor(999999 / 261120) = 3 - 需查
n为[1, 2, 3]的chunks文档,再对每个chunk的data字段做字节切片
如果用for循环+findOne,3个chunk就要3次往返,延迟叠加。改用find({files_id: fileId, n: {$in: [1,2,3]}})一次拉回,再按n排序处理,吞吐明显提升。
注意data字段是BinData类型,Node.js驱动返回的是Buffer,Python是bytes,切片直接用.slice(start, end)即可,无需base64编解码。
响应头必须手动设全:Content-Range、Accept-Ranges、Content-Length
浏览器判断能否断点续传,只看响应头,不看内容。漏掉任意一个,curl -C或下载工具就会重头开始。
-
Accept-Ranges: bytes:告诉客户端“我支持Range” -
Content-Range: bytes 500000-999999/1234567:格式严格,末尾总长度不能错(来自files.length) -
Content-Length:必须等于实际返回的字节数(即999999 - 500000 + 1),不是原始文件大小 - 状态码必须是
206 Partial Content,不是200
常见错误:用files.length当Content-Length,或者把Content-Range写成bytes 500000-999999(缺/总长),Chrome会静默失败,curl -v能看到HTTP/1.1 206但没数据。
并发Range请求下,chunkSize不一致会导致切片错位
同一个GridFS bucket里,不同文件可以设不同chunkSize(比如上传时指定chunkSizeBytes: 1024*1024)。如果你缓存了某个文件的chunkSize但没绑定files_id,后续请求另一个chunkSize不同的文件,用错值去算n,结果就是读到错误的chunk、拼出乱码。
安全做法是:每次Range请求都先查files集合拿当前文件的chunkSize和length,不要复用上次结果。虽然多一次查询,但避免数据错位这种致命问题。
另外,MongoDB 6.0+支持$lookup聚合直接关联files和chunks,但Range切片逻辑复杂,聚合难表达偏移计算,不如两次查询清晰可控。











