不能直接用openDownloadStream生成临时链接,因GridFS无签名功能,必须由后端构造带exp和sig参数的CDN URL,并通过中间件校验token、查询文件、流式响应,否则会导致未授权无限下载。

不能直接生成临时链接,必须由后端构造带签名的 URL 并配中间件校验——GridFS 本身不提供任何链接生成功能。
为什么不能用 openDownloadStream 直接返回 HTTP 响应
常见错误是把 bucket.openDownloadStream(fileId) 的流直接 pipe 给 res,这等于把整个 GridFS 暴露在公网:没有时效控制、无权限校验、无法限频、无法审计。一旦 URL 泄露,攻击者可无限次下载任意文件。
- 必须拦截所有
/files/:id或/f/:cdn_key类请求,先校验 token 再查文件再流式响应 -
openDownloadStream只负责读取,不负责鉴权;鉴权逻辑必须写在路由 handler 或中间件里 - 若用
openDownloadStreamById,传入的fileId必须是ObjectId实例,不能是字符串——URL 解析出的 ID 需显式转换:new ObjectId(idStr)
临时 URL 应该长什么样、含哪些参数
推荐格式:https://cdn.example.com/f/{cdn_key}?exp=1734772260&sig=abc123...,其中 cdn_key 是业务层生成的稳定标识(如 video_8a9b2c),不是 _id。
-
exp是 Unix 时间戳,服务端校验Date.now() -
sig是 HMAC-SHA256 签名,输入为cdn_key + ':' + exp,密钥从环境变量读取(如GRIDFS_TOKEN_KEY) - 不要暴露
_id:它含时间戳和机器信息,易被枚举;也不要用filename做路径,因无唯一索引,冲突难处理 - 前端或 CDN 缓存需禁用:
Cache-Control: no-store, must-revalidate,避免 token 被缓存重放
如何用 cdn_key 查文件并安全流式响应
GridFS Bucket 不支持按 metadata.cdn_key 直接打开流,必须两步走:先查 fs.files 得到 _id,再用 openDownloadStream。
- 确保已建索引:
db.fs.files.createIndex({"metadata.cdn_key": 1}, {unique: true, sparse: true}) - 查询时加
{ projection: {_id: 1} },只取 ID,减少传输开销 - 查不到或
_id无效时,必须返回404或403,不能静默 fallback - 响应头要设
Content-Type(从fileDoc.metadata.contentType取)和Content-Length(fileDoc.length),否则浏览器/播放器可能无法正确解析音视频
音视频场景下容易忽略的细节
音视频文件对 HTTP Range 请求、Content-Type、缓存策略更敏感,光“能下载”不等于“能播放”。
- 必须支持
Range请求:Node.js 中用stream.pipe(res)默认不支持断点续传;要用pipe+http-errors+ 手动解析Range头,或改用express-static-range类库 -
contentType必须准确:MP4 是video/mp4,MP3 是audio/mpeg,错成application/octet-stream会导致 iOS Safari 拒绝内建播放器加载 - 不要用
nginx-gridfs模块替代签名逻辑:它绕过应用层,无法校验 token 和权限,仅适合内部可信网络下的静态分发 - 大音视频文件建议设置
socket.setTimeout(300000),避免流式传输中途连接超时中断


















