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
相关产品推荐
相关产品推荐

