Node.js Express返回流式数据客户端未按分块读取问题咨询
核心原因
你的流式传输逻辑本身符合规范,出现前端无法实时接收的问题,基本是中间环节存在缓冲导致的,常见原因如下:
- 服务端启用了响应压缩中间件(如
compression),默认会攒够一定大小的响应内容后才会压缩输出,阻断了实时分块返回 - 缺少必要的响应头配置,导致浏览器/反向代理节点默认缓冲了响应内容
- 部署架构中存在Nginx等反向代理,默认开启了上游响应缓冲,会等服务端返回完整响应后才发给浏览器
- 客户端封装的
wpeFetch存在全量读取响应的逻辑,没有透传流式可读对象
修复方案
服务端调整
在原有代码基础上修改响应头配置,同时关闭当前接口的压缩,修改后的服务端代码如下:
app.put('/api/action', (req, res) => { const spawn = require('child_process').spawn; // 关闭当前接口的压缩,避免compression中间件缓冲 res.setHeader('Content-Encoding', 'identity'); res.setHeader('Content-Type', 'text/plain; charset=utf-8'); res.setHeader('Transfer-Encoding', 'chunked'); // 禁止缓存和内容转换,避免中间节点缓冲 res.setHeader('Cache-Control', 'no-cache, no-transform'); res.setHeader('Connection', 'keep-alive'); const child = spawn('./my-shell-script', [], { cwd: path.resolve(__dirname, '../scripts/'), shell: true }); child.stdout.on('data', data => { const content = data.toString(); console.log('write data', content); res.write(content); // 可选:手动刷出响应缓冲,确保内容立即发送 res.flush?.(); }); child.stderr.on('data', error => { const errContent = error.toString(); console.log('write error', errContent); res.write(errContent); res.flush?.(); }); child.on('error', s => console.error('error', s)); child.on('close', (code) => { console.log(`action exited with code ${code}`); if (code !== 0) { res.status(500); } res.end(); }); });
反向代理配置调整(存在Nginx部署场景)
在接口对应的Nginx location配置段中添加以下规则,关闭代理缓冲:
location /api/action { proxy_pass http://你的Express服务地址; proxy_buffering off; proxy_set_header Connection keep-alive; }
客户端验证调整
先替换wpeFetch为原生fetch测试流式接收是否正常,如果原生fetch可以正常逐块打印内容,说明你封装的wpeFetch内部做了全量读取响应的逻辑,需要修改封装逻辑透传response.body可读流。
原生fetch测试代码如下:
const response = await fetch(`/api/action`, { method: 'PUT', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify({ scope: site, action, }) }); const reader = response.body.pipeThrough(new window.TextDecoderStream()).getReader(); while (true) { const { value, done } = await reader.read(); if (done) break; console.log('Received', value); } console.log('Response fully received');
内容的提问来源于stack exchange,提问作者Matt Dietsche
相关产品推荐
相关产品推荐

