本地正常的HTML转PDF API部署Azure Web Services后超时故障排查
问题分析与解决方案
1. 修复 TypeError: Cannot read properties of undefined (reading 'status') 错误
该错误根源是回调函数参数名与外层Express响应对象res重名:在pdf.create的回调中,你用了res作为参数,覆盖了外层的Express响应对象。当生成PDF出错时,回调内的res并非Express响应对象,而是html-pdf返回的错误结果(此时为undefined),调用res.status()自然会报错。
修复后的代码:
function generatePDF(req, res) { const htmlContent = req.body.html; return new Promise((resolve, reject) => { // 将回调参数res改为result,避免覆盖外层响应对象 pdf.create(htmlContent, options).toFile("./public/docs/doc.pdf", (err, result) => { if (err) { console.log(err); res.status(500).send("Une erreur s'est produite lors de la creation du fichier Pdf"); reject(err); // 出错时用reject传递错误,而非resolve } else { console.log("Le fichier Pdf a été généré dans le serveur"); resolve(result); } }); }); }
额外优化:
- 原代码中包裹Promise的
try/catch无效,因为pdf.create是异步操作,错误不会被外层同步try/catch捕获,直接删除即可。 - 出错时调用
reject(err),让外层await能捕获错误,避免后续执行readPDF读取不存在的文件。
2. 解决Azure环境中html-pdf: PDF generation timeout. Phantom.js script did not exit错误
本地正常但Azure超时,核心是PhantomJS在Azure App Service环境的兼容性或配置问题,可按以下顺序尝试方案:
方案1:延长PhantomJS超时时间
默认超时时间可能不足以在Azure环境完成PDF生成,在配置中增加超时设置:
const options = { "height": "14cm", "width": "20cm", "border": "0", "header": { "height": "0mm" }, "footer": { "height": "0mm" }, "timeout": 30000 // 延长至30秒,可根据实际情况调整 }
方案2:检查文件路径与写入权限
Azure App Service的文件系统有读写限制,建议使用临时目录或确保目标目录存在:
const fs = require('fs'); const path = require('path'); // 优先使用Azure临时目录,避免权限问题 const savePath = process.env.TEMP || path.join(__dirname, '../public/docs'); // 确保目录存在,不存在则创建 fs.mkdirSync(savePath, { recursive: true }); // 修改保存路径 pdf.create(htmlContent, options).toFile(path.join(savePath, 'doc.pdf'), ...);
方案3:切换Azure App Service至Linux环境
PhantomJS在Windows环境下兼容性较差,切换到Linux环境大概率能解决超时问题:
- 登录Azure门户,找到你的Web App
- 进入「配置」>「常规设置」
- 将「操作系统」改为Linux,保存后重新部署代码
方案4:替换为维护中的PDF生成库
node-html-pdf已停止维护(最后更新于2018年),依赖的PhantomJS也已废弃。推荐替换为Puppeteer(基于Chromium),在Azure环境稳定性更强:
const puppeteer = require('puppeteer'); async function generatePDF(req, res) { const htmlContent = req.body.html; const browser = await puppeteer.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox'] // Azure环境必需参数 }); const page = await browser.newPage(); await page.setContent(htmlContent); const pdfBuffer = await page.pdf({ height: "14cm", width: "20cm", margin: { top: 0, bottom: 0, left: 0, right: 0 } }); await browser.close(); return pdfBuffer; } exports.generatePDFfromHTML = async function (req, res) { try { const pdfBuffer = await generatePDF(req, res); res.status(200).send(pdfBuffer.toString('base64')); } catch (err) { console.log(err); res.status(500).send("Erreur lors de la génération du PDF"); } }
3. 额外优化:避免无效的文件读取
原代码中generatePDF失败后仍会执行readPDF,导致读取不存在的文件。修改外层函数,用try/catch控制流程:
exports.generatePDFfromHTML = async function (req, res) { try { await generatePDF(req, res); readPDF(req, res); } catch (err) { console.log(err); res.status(500).send("Erreur lors de la génération du PDF"); } }
内容的提问来源于stack exchange,提问作者Yannick Makwenge
相关产品推荐
相关产品推荐

