Node.js对接AWS S3调用getObject时报Cannot set headers after they are sent错误
问题原因
[ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client 错误本质是HTTP响应头部已经发送给客户端后,代码再次尝试修改响应状态、响应头或写入响应内容导致的冲突。
你的代码触发问题的具体场景:
- S3文件流通过
pipe方法传给响应对象res时,只要流开始传输第一个字节的数据,响应头就会自动发送给客户端。此时如果S3流抛出错误,error回调中执行的res.status(500).json()会尝试修改已发送的响应,直接触发报错。 - 函数标记了
async但内部没有await任何异步操作,也没有做顶层错误捕获,如果s3.getObject或createReadStream的同步执行阶段抛出错误,也会导致响应处理逻辑冲突。 - 没有处理请求提前中断的场景,如果用户在下载过程中取消请求,S3流仍会继续传输,可能触发后续的响应写入错误。
修复方案
修改后的代码新增了响应头发送状态判断、全局错误捕获和请求中断处理逻辑,可解决偶现报错问题:
export const downloadFile = async(req: Request, res: Response) => { let s3Stream = null; try { res.setHeader('Content-Disposition', 'attachment'); const params = { Bucket: AWS_S3.Bucket, Key: req.params.key }; // 可选:提前拉取文件元信息,设置正确的响应头,降低传输中途出错概率 const headData = await s3.headObject(params).promise(); res.setHeader('Content-Length', headData.ContentLength); res.setHeader('Content-Type', headData.ContentType || 'application/octet-stream'); s3Stream = s3.getObject(params).createReadStream(); // 处理S3流错误 s3Stream.on('error', error => { if (!res.headersSent) { return res.status(500).json({ message: 'An error ocurred...', error }); } res.end(); }); // 处理用户提前中断请求的场景,销毁流避免内存泄漏 req.on('close', () => { s3Stream?.destroy(); }); s3Stream.pipe(res); } catch (error) { // 捕获同步错误和元信息拉取的异步错误 if (!res.headersSent) { return res.status(500).json({ message: 'An error ocurred...', error }); } s3Stream?.destroy(); res.end(); } }
内容的提问来源于stack exchange,提问作者Bladimir Medrano Vargas
相关产品推荐
相关产品推荐

