本地正常运行的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')
问题排查与解决
核心原因
- 异步逻辑冲突:你混用了
await和回调函数,pdf.create(...).toStream()的回调执行时机与外层await不兼容,导致stream未正确初始化。 - 忽略错误回调:回调的第一个参数
err未处理,PDF生成过程中可能因Heroku环境依赖缺失、资源不足等问题抛出错误,直接导致stream为undefined。 - 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
相关产品推荐
相关产品推荐

