You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS中request.on('data')事件延迟触发的原因及解决方案

问题原因

  • 全局Body解析中间件的缓冲机制:如果你在代码其他位置全局挂载了express.json()、express.urlencoded()或者multer这类请求体解析中间件,这类中间件默认会先完整接收整个请求体、缓冲到内存中,全部读取完成后才会将请求传递到你的/upload路由回调。此时你再监听request的data事件,相当于监听一个已经被读取完成的流,自然只能等到所有数据都被中间件接收完才会触发事件,小文件缓冲快所以感知不到延迟,大文件缓冲耗时就会出现明显延迟。
  • 响应提前结束:你的代码在路由回调的同步逻辑中直接调用了response.send(),这个方法会直接结束当前HTTP响应会话。对于小文件,请求体在TCP连接建立初期就已经全部到达服务器内核缓冲区,data事件能在响应结束前快速触发完成;大文件传输时,response.send()先执行,内核会自动丢弃后续传输的数据或者静默消费缓冲,导致data事件无法实时触发。
  • 反向代理缓冲:如果你的服务前置了Nginx、CDN这类反向代理服务,默认配置下反向代理会先完整接收客户端上传的全部数据,缓冲到本地磁盘/内存后才会转发给你的Node.js服务,此时服务端自然只能等到所有数据都到达反向代理后才会触发data事件。

修复方案

1. 调整Body解析中间件的挂载范围

不要对/upload路由使用任何预请求体解析中间件,如果你是全局挂载的,可以修改为只对需要解析的路由生效,或者对上传路由排除处理:

// 错误写法:全局挂载,所有路由都会先缓冲请求体
// app.use(express.json());
// app.use(express.urlencoded({ extended: true }));

// 正确写法:仅给非上传路由挂载解析中间件
app.use(['/api/*', '/other-routes'], express.json());
// 上传路由不挂载任何body解析中间件,直接处理原始流

2. 调整响应逻辑,使用流式响应返回进度

不要提前调用response.send()结束响应,改为流式传输实时推送进度,全部数据接收完成后再结束响应:

const app = express();
const port = 3000;

app.post('/upload', function (request, response) {
    // 设置流式响应头,避免客户端缓冲返回内容
    response.setHeader('Content-Type', 'text/plain');
    response.setHeader('Transfer-Encoding', 'chunked');

    let receivedLength = 0;
    const totalLength = parseInt(request.headers['content-length'] || 0);

    request.on('data', chunk => {
        receivedLength += chunk.length;
        console.log('upload on data', chunk.length, `进度: ${((receivedLength/totalLength)*100).toFixed(2)}%`);
        // 实时给客户端推送进度信息
        response.write(`received: ${receivedLength}, total: ${totalLength}\n`);
    });

    // 全部接收完成后再结束响应
    request.on('end', () => {
        response.end(JSON.stringify({
            message: `上传完成,总大小 ${totalLength}`
        }));
    });

    // 处理请求异常
    request.on('error', (err) => {
        response.statusCode = 500;
        response.end(JSON.stringify({error: err.message}));
    });
});

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`);
});

3. 关闭反向代理的请求缓冲(如有)

如果使用了Nginx作为反向代理,在对应的站点配置中添加如下规则,关闭上传路由的请求缓冲:

location /upload {
    proxy_pass http://127.0.0.1:3000;
    proxy_request_buffering off;
}

内容的提问来源于stack exchange,提问作者WhatIsHTML

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 10:57:00