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

本地正常运行的PDF生成代码在Heroku生产环境报pipe未定义错误

问题描述

本地环境运行PDF生成代码正常,但部署到Heroku生产服务器后立即报错,错误类型为TypeError: Cannot read properties of undefined (reading 'pipe')。

实现代码

await pdf.create(pdfTemplate(data)).toStream(async function (err, stream) {
      await stream.pipe(fs.createWriteStream(`${req.body.phone}.pdf`));
      const params = {
        Bucket: "icon-path-bucket",
        Body: stream,
        Key: req.body.phone,
        contentType: "application/pdf"
      }
});

生产环境错误信息

2023-06-17T10:16:06.144530+00:00 app[web.1]:       await stream.pipe(fs.createWriteStream(`${req.body.phone}.pdf`));
2023-06-17T10:16:06.144530+00:00 app[web.1]:                    ^
2023-06-17T10:16:06.144530+00:00 app[web.1]: 
2023-06-17T10:16:06.144531+00:00 app[web.1]: TypeError: Cannot read properties of undefined (reading 'pipe')

问题排查与解决

核心原因

  1. 异步逻辑冲突:你混用了await和回调函数,pdf.create(...).toStream()的回调执行时机与外层await不兼容,导致stream未正确初始化。
  2. 忽略错误回调:回调的第一个参数err未处理,PDF生成过程中可能因Heroku环境依赖缺失、资源不足等问题抛出错误,直接导致stream为undefined。
  3. Heroku文件系统限制:Heroku的本地文件系统是临时的,写入操作可能失败;且流是一次性资源,同时pipe到本地文件和S3会导致流被耗尽,后续使用时stream已失效。

解决方案

方案1:改用Promise风格处理(推荐)

如果PDF生成库支持Promise,直接用async/await替代回调,同时跳过本地文件写入(Heroku无需本地存储,直接将流上传到S3):

// 生成PDF流
const stream = await pdf.create(pdfTemplate(data)).toStream();

// 配置S3上传参数,修正参数命名和文件后缀
const params = {
  Bucket: "icon-path-bucket",
  Body: stream,
  Key: `${req.body.phone}.pdf`, // 补充.pdf后缀,确保S3识别文件类型
  ContentType: "application/pdf" // AWS SDK要求驼峰命名,修正为ContentType
};

// 执行S3上传(假设使用aws-sdk)
await s3.upload(params).promise();

方案2:修复回调逻辑(兼容旧版本库)

若必须使用回调,需先处理错误,且避免重复使用流:

pdf.create(pdfTemplate(data)).toStream(function (err, stream) {
  // 优先处理生成错误
  if (err) {
    console.error("PDF生成失败:", err);
    return res.status(500).send("PDF生成失败");
  }

  // 使用Heroku临时目录/tmp存储文件,避免写入权限问题
  const tempFilePath = `/tmp/${req.body.phone}.pdf`;
  const writeStream = fs.createWriteStream(tempFilePath);

  // 等待文件写入完成后再读取上传
  stream.pipe(writeStream).on('finish', async function() {
    const fileStream = fs.createReadStream(tempFilePath);
    const params = {
      Bucket: "icon-path-bucket",
      Body: fileStream,
      Key: `${req.body.phone}.pdf`,
      ContentType: "application/pdf"
    };
    await s3.upload(params).promise();
  });
});

额外注意事项

  • Heroku依赖配置:确保PDF生成库的底层依赖(如phantomjs、Chrome等)已通过Heroku buildpack安装,例如添加heroku-buildpack-phantomjs。
  • 流的单次使用:Node.js流是一次性资源,不能同时pipe到多个目标,如需复用需使用duplex流复制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 12:30:17