Node.js报错ERR_HTTP_HEADERS_SENT问题求助
Node.js 端频繁崩溃:ERR_HTTP_HEADERS_SENT 错误
错误日志
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at new NodeError (node:internal/errors:406:5) at ServerResponse.setHeader (node:_http_outgoing:652:11) at ServerResponse.header (C:\Users\devgarg\Downloads\Projects\React Projects\CENACLE\server\node_modules\express\lib\response.js:776:10) at ServerResponse.json (C:\Users\devgarg\Downloads\Projects\React Projects\CENACLE\server\node_modules\express\lib\response.js:264:10) at PassThrough.<anonymous> (file:///C:/Users/devgarg/Downloads/Projects/React%20Projects/CENACLE/server/controller/post.js:32:17) at PassThrough.emit (node:events:526:35) at PassThrough.emit (node:domain:488:12) at Request.<anonymous> (C:\Users\devgarg\Downloads\Projects\React Projects\CENACLE\server\node_modules\aws-sdk\lib\request.js:590:14) at Request.callListeners (C:\Users\devgarg\Downloads\Projects\React Projects\CENACLE\server\node_modules\aws-sdk\lib\sequential_executor.js:106:20) at Request.emit (C:\Users\devgarg\Downloads\Projects\React Projects\CENACLE\server\node_modules\aws-sdk\lib\sequential_executor.js:78:10) { code: 'ERR_HTTP_HEADERS_SENT', time: 2024-04-24T17:46:46.189Z } Node.js v20.9.0
问题背景
错误指向 server/controller/post.js 第32行,该函数负责从AWS S3存储桶获取图片并通过流返回给客户端。已尝试终止重复请求、添加错误处理逻辑,但问题仍未解决。
相关代码
let readStreams = {}; export const getPost = (req, res) => { const Key = req.path.split('/')[1]; // 终止同Key的现有流 if (readStreams[Key]) { readStreams[Key].destroy(); } readStreams[Key] = getFile(Key); // 标记响应是否已发送 let responseSent = false; // 错误处理 readStreams[Key].on("error", (err) => { if (!responseSent) { res.json({ error: "读取流时发生错误" }); responseSent = true; } }); // 将流pipe到响应 readStreams[Key].pipe(res); // 流结束后关闭并清理 readStreams[Key].on("end", () => { if (!responseSent) { readStreams[Key].destroy(); responseSent = true; } delete readStreams[Key]; }); }
问题分析
这个错误的核心是在已经向客户端发送响应后,尝试再次设置HTTP响应头,具体原因:
- 执行
stream.pipe(res)后,响应头已发送给客户端,此时若流触发error事件,调用res.json()会再次尝试发送头信息,导致冲突。 - 全局对象
readStreams管理不严谨,多个请求同时操作同一个Key时,易出现流状态混乱(比如旧流未完全销毁就被覆盖)。 end事件中调用destroy()冗余,流结束后会自动销毁,此时响应已完成,设置responseSent无实际意义。
解决方案
修改代码优化流管理和错误处理逻辑,避免重复发送响应头:
const readStreams = new Map(); // 用Map替代普通对象,避免属性冲突和类型问题 export const getPost = (req, res) => { const Key = req.path.split('/')[1]; // 终止同Key的现有流并清理 if (readStreams.has(Key)) { const existingStream = readStreams.get(Key); if (!existingStream.destroyed) { existingStream.destroy(); } readStreams.delete(Key); } const stream = getFile(Key); readStreams.set(Key, stream); // 错误处理:仅在响应头未发送时设置状态码并结束响应 stream.on("error", () => { if (!res.headersSent) { res.status(500).end("读取图片时发生错误"); } // 仅清理当前请求对应的流记录 if (readStreams.get(Key) === stream) { readStreams.delete(Key); } }); // 流结束时清理记录 stream.on("end", () => { if (readStreams.get(Key) === stream) { readStreams.delete(Key); } }); // 客户端断开连接时销毁流,避免内存泄漏 res.on("finish", () => { if (!stream.destroyed) { stream.destroy(); } readStreams.delete(Key); }); // 将流pipe到响应 stream.pipe(res); };
修改说明
- 改用
Map存储流,比普通对象更安全,避免Key的隐式类型转换和意外属性覆盖 - 错误处理时先检查
res.headersSent,确保仅在响应未发送时修改状态,避免触发头信息重复发送错误 - 移除冗余的
responseSent标志,直接用Node.js原生的res.headersSent判断响应状态 - 添加
res.finish事件监听,客户端断开连接时主动销毁流,避免内存泄漏 - 流结束或销毁时,严格匹配当前流再清理记录,避免误删其他请求的流
内容的提问来源于stack exchange,提问作者Dev Garg
相关产品推荐
相关产品推荐

