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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 19:15:02