解决Firebase云函数生成动态页面PDF内容不全问题
解决Firebase Functions中Puppeteer生成PDF内容截断问题
针对本地能生成完整150页PDF、线上Firebase环境仅生成30/50页的问题,从页面加载、Puppeteer配置、函数资源三个方向给出解决方案:
1. 修复页面内容加载不完整问题
线上环境网络和渲染速度比本地慢,当前的等待逻辑可能没覆盖所有异步内容加载:
- 处理懒加载内容:如果页面单元内容是滚动加载,Puppeteer默认不会触发滚动,导致下方内容未渲染。添加滚动逻辑:
// 在waitForSelector('div.content-has-loaded')之后添加 await page.evaluate(async () => { await new Promise((resolve) => { let totalHeight = 0; const distance = 1000; const timer = setInterval(() => { const scrollHeight = document.body.scrollHeight; window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= scrollHeight) { clearInterval(timer); resolve(); } }, 200); }); }); // 滚动后等待内容渲染 await delay(1000);
- 强化等待逻辑:将
waitUntil: "networkidle2"改为waitUntil: "networkidle0",确保所有网络请求完成;同时增加对图片加载完成的检查:
await page.waitForFunction(() => { const imgs = document.querySelectorAll('img'); return Array.from(imgs).every(img => img.complete); });
2. 优化Puppeteer启动参数
Firebase Functions的容器环境有资源限制,调整启动参数避免渲染异常:
const browser = await puppeteer.launch({ args: [ "--no-sandbox", "--disable-dev-shm-usage", // 解决容器/dev/shm空间不足问题 "--disable-gpu", "--disable-setuid-sandbox", "--window-size=1920,1080" // 设置固定视口,避免自适应布局导致内容截断 ], headless: 'new', // 使用新版无头模式,渲染更稳定 });
3. 验证函数资源与超时配置
- 确认
timeoutSeconds: 120是否足够:生成150页PDF需要较长时间,可尝试调整到300秒(Firebase Functions最大超时) - 检查函数日志:在Firebase控制台查看函数执行日志,确认是否有内存不足、超时或渲染报错信息,若有内存警告可尝试申请更高内存配额(最大8GB)
4. 调试线上渲染状态
在生成PDF前添加全屏截图,验证页面是否完整渲染:
// 在page.pdf()之前添加 const screenshot = await page.screenshot({ fullPage: true }); // 将截图上传到Firebase Storage查看 await admin.storage().bucket().file(`debug/${courseID}-${sectionID}.png`).save(screenshot);
修改后的核心代码片段
// ... 原有代码 ... const browser = await puppeteer.launch({ args: [ "--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu", "--disable-setuid-sandbox", "--window-size=1920,1080" ], headless: 'new', }); let page = await browser.newPage(); await page.goto(url, { waitUntil: "networkidle0" }); await page.waitForSelector('div.section-pdf-container') await page.waitForSelector('#section-title') await page.waitForSelector('div.content-has-loaded') // 处理懒加载内容 await page.evaluate(async () => { await new Promise((resolve) => { let totalHeight = 0; const distance = 1000; const timer = setInterval(() => { const scrollHeight = document.body.scrollHeight; window.scrollBy(0, distance); totalHeight += distance; if (totalHeight >= scrollHeight) { clearInterval(timer); resolve(); } }, 200); }); }); await delay(1500); // 等待渲染完成 // 等待所有图片加载 await page.waitForFunction(() => { const imgs = document.querySelectorAll('img'); return Array.from(imgs).every(img => img.complete); }); const pdf = await page.pdf({ displayHeaderFooter: true, headerTemplate: '<style>html { -webkit-print-color-adjust: exact;}</style><div style="font-size: 9px; color:rgba(0,0,0,.35); padding-left: 30px;">The Journey: ' + unitTitle + '</div>', footerTemplate: '<style>html { -webkit-print-color-adjust: exact;}</style><div style="font-size: 9px; color:rgba(0,0,0,.35); padding-right: 30px; width:100%; text-align:right;">Page <span class="pageNumber"></span> of <span class="totalPages"></span></div>', printBackground: true, margin: { top: '0.5in', bottom: '0.5in', left: '0.3in', right: '0.3in' } }); // ... 原有代码 ...
内容的提问来源于stack exchange,提问作者Jason
相关产品推荐
相关产品推荐

