GridFS存储的视频不能直接用<video>标签播放,因其不支持HTTP Range请求,需服务端代理解析Range头、读取对应字节范围并返回206响应及正确Content-Range等头部。

GridFS 存储的视频为什么不能直接 <video> 标签播放
因为 GridFS 把大文件切分成多个 chunks 文档存进 fs.chunks,元信息存在 fs.files,它本身不提供 HTTP 范围请求(Range)支持,而浏览器播放器依赖这个机制做拖拽、缓冲和首帧加载。直接返回整个文件流会卡死、无法拖动、甚至 404。
必须用服务端代理转发 + 支持 Range 请求
你得写一个路由(比如 Express 的 /api/video/:id),根据 _id 或 filename 查 GridFS 文件,再手动读取对应字节范围并返回带 Content-Range 和 206 Partial Content 的响应。
- 用官方驱动的
GridFSBucket.openDownloadStream()获取可读流,但别直接 pipe —— 它不自动处理Range - 必须解析请求头里的
Range: bytes=xxx-yyy,计算 chunk 偏移,跳过前面若干chunks,再从某个chunk的中间位置开始读 - 注意:MongoDB 驱动 v4+ 的
openDownloadStream()返回的是ReadableStream,不是 Node.js 原生stream.Readable,需用stream.Readable.from()包一层才能用pipe或on('data') - 返回时务必设置
Content-Type(如video/mp4)、Accept-Ranges: bytes、Content-Range和状态码206
mongofiles 和直接读 fs.files 都不行
mongofiles 是命令行工具,只支持完整上传/下载,没有 HTTP 接口;直接查 fs.files 只能拿到元数据,没内容。有人试过把整个文件 load 到内存再切片返回,这在视频 >100MB 时极易 OOM,Node.js 进程直接崩。
- 别用
findOne({ _id })查fs.files后自己拼chunks—— 缺少分片顺序校验,出错难定位 - 别在前端用
fetch拿到全部二进制再喂给URL.createObjectURL()—— 大文件会卡 UI,且无法拖拽 - 真实场景中,iOS Safari 对
Content-Range格式更敏感,结尾要带总长度(如bytes 0-999/1234567),不能写bytes 0-999/*
小文件(
base64 体积膨胀 ~33%,1MB 视频变 1.3MB 字符串,HTTP 响应体更大,JS 解析也慢,而且依然绕不开 Range —— 浏览器不会对 base64 字符串做字节范围请求。真正省事的方案只有两个:用 Nginx + mongo-gridfs 模块(已废弃,不推荐),或者老老实实写支持 Range 的代理。
- 如果用 Express,推荐封装一个
createGridFSVideoStream()工具函数,把 range 解析、chunk 跳转、流控制逻辑收进去 - 注意 MongoDB 连接池配置:视频并发多时,
maxPoolSize至少设为 20+,否则容易阻塞 - 测试时用
curl -H "Range: bytes=0-1023" http://localhost:3000/api/video/xxx看是否返回206和正确长度,比在浏览器里瞎点靠谱得多
Content-Range 头、算错偏移、chunk 读取跨边界、没设 Accept-Ranges —— 任一环节断了,进度条就卡住。


















