Express/request如何流式代理超大文件 无需服务器端全量下载
Node.js大视频分块流式代理实现方案
原代码问题根因
你当前的代码出现「服务器先缓存全量文件再发送给客户端」的问题,核心原因有三点:
- 没有处理HTTP
Range请求头:视频客户端(浏览器、播放器)默认会携带Range头请求文件分块,你的代码没有透传该头到源站,源站会返回完整15GB文件的200响应,客户端收到不带Accept-Ranges标识的响应后,会默认等待全量文件接收完成才开始处理 - 大概率被响应缓冲中间件拦截:如果你的路由前挂载了
compression、body-parser等会缓冲响应体的中间件,会自动把流式响应暂存在内存/磁盘,等全量接收完成后才发送给客户端,直接破坏流式传输逻辑 - 已废弃的
request库本身存在流控缺陷,大文件传输时容易出现内存堆积问题。
最优实现方案
核心原则是透传分块相关头、不做任何响应缓冲、直接流式转发,Node.js 18+ 可以直接用内置的fetch实现,无需额外安装第三方请求库:
const { Readable } = require('node:stream'); const express = require('express'); const cors = require('cors'); const app = express(); // 注意:该路由必须注册在compression、body-parser等会缓冲响应的中间件之前 app.all('/shareRedirects/*', cors({origin: '*'}), async (req, res) => { // 替换为你实际的源站基础地址 const ORIGIN_BASE = 'https://你的源站存储地址/'; const targetUrl = new URL(req.params[0], ORIGIN_BASE).toString(); try { // 透传分块请求头到源站 const upstreamResp = await fetch(targetUrl, { method: req.method, headers: { ...(req.headers.range ? { Range: req.headers.range } : {}), // 源站需要鉴权的话在这里加对应鉴权头即可 }, highWaterMark: 1024 * 1024, // 单块大小设为1MB,平衡内存占用和传输性能 }); // 原样返回源站的状态码(分块请求会返回206 Partial Content) res.status(upstreamResp.status); // 透传分块传输核心响应头 const PASS_HEADERS = ['content-type', 'content-length', 'content-range', 'accept-ranges', 'cache-control', 'last-modified', 'etag']; PASS_HEADERS.forEach(headerKey => { const headerVal = upstreamResp.headers.get(headerKey); if (headerVal) res.setHeader(headerKey, headerVal); }); // 直接将上游流转发到客户端响应,不做全量缓存 const upstreamStream = Readable.fromWeb(upstreamResp.body); upstreamStream.pipe(res); // 处理连接断开的资源释放 res.on('close', () => upstreamStream.destroy()); upstreamStream.on('error', (err) => { if (!res.headersSent) res.sendStatus(500); upstreamStream.destroy(); }); } catch (err) { if (!res.headersSent) res.sendStatus(500); } }); app.listen(3000);
关键注意事项
- 禁止给该路由挂载响应压缩中间件:压缩逻辑需要读取完整响应体才能计算压缩结果,会强制触发全量缓冲,15GB文件会直接占满服务器内存
- 单块大小
highWaterMark不要设置超过2MB,否则高并发场景下内存占用会快速升高 - 该方案天然支持视频进度条拖动、断点续传,不需要额外写逻辑处理分块计算,所有分块逻辑由源站处理,代理层只做透传
- 如果使用Node.js 18以下版本,把内置fetch替换为
undici库的fetch实现即可,性能和流稳定性远高于已废弃的request库
内容的提问来源于stack exchange,提问作者born2net
相关产品推荐
相关产品推荐

