生产环境上传PDF Buffer到S3时报首个参数类型不匹配错误
问题根因
报错本质是传入Buffer.from()的pdf参数不是合法的字符串/二进制类型,而是一个Error对象。
问题出在getPDFBuffer的错误处理逻辑:生产环境下chrome-aws-lambda生成PDF流程触发异常时,catch块直接return error,没有抛出错误,导致外层把Error实例当成正常的PDF Buffer传到了上传步骤,调用Buffer.from(error)时就触发了类型不匹配错误。
本地环境用原生puppeteer时PDF生成流程没有异常,能拿到正常Buffer,所以上传流程可以跑通。生产环境chrome-aws-lambda启动失败、依赖缺失、参数配置错误都可能导致PDF生成步骤抛错,被错误的catch逻辑吞掉后,就会触发你看到的上传报错。
另外原代码里还有两个隐藏问题:
waitForNavigation写在setContent之前,存在竞态,生产环境偶发会出现页面没加载完就生成PDF、甚至等待超时的问题page.pdf()本身返回的就是Buffer类型,上传时不需要再额外调用Buffer.from()做转换
修复方案
- 修正
getPDFBuffer的错误处理,不要吞错,同时修复页面加载的竞态问题:
static getPDFBuffer = async (html, options) => { let browser = null; try { const executablePath = process.env.IS_OFFLINE ? null : await chromium.executablePath; browser = await chromium.puppeteer.launch({ args: chromium.args, executablePath, headless: true, ignoreDefaultArgs: ['--disable-extensions'] }); const page = await browser.newPage(); // 把等待逻辑合并到setContent配置中,解决竞态 await page.setContent(html, { waitUntil: "load" }); return await page.pdf({ format: 'A4', printBackground: true, ...options }); } catch (error) { console.error("PDF生成失败:", error); // 直接抛出错误,不要返回Error对象 throw error; } finally { if (browser !== null) { await browser.close(); } } };
- 调用
getPDFBuffer的位置增加错误捕获,避免异常值流入后续流程:
let pdf; try { pdf = await Helper.getPDFBuffer(html, options); } catch (e) { throw new Error(`PDF生成失败:${e.message}`); } // 原有S3预签名URL生成逻辑保持不变 const merchantCod = params.merchantId; const signedURLExpireSeconds = 60 * 10; const rutaDocumento = params.folderMovement + "/" const tipoDocumento = "facturaNro" const extraRandomNumber = Math.floor(Math.random() * (10000 - 1)) + 1; let nroDocumento = params.nro const presignedS3UrlPDF = await s3getSignedUrl("putObject", { Bucket: BUCKET_NAME, Key: "another/" + merchantCod + "/" + rutaDocumento + tipoDocumento + nroDocumento + "-" + extraRandomNumber+ ".pdf", Expires: signedURLExpireSeconds, ContentType: "application/pdf" }); let resultUploadPDF = await uploadPDF(presignedS3UrlPDF, pdf);
- 优化上传方法,增加类型校验,去掉冗余的Buffer转换:
static async uploadPDF(presignedS3Url, pdf){ try { if (!Buffer.isBuffer(pdf) && typeof pdf !== 'string') { throw new Error(`待上传内容类型非法,实际类型:${typeof pdf}`); } // page.pdf已经返回Buffer,直接传入即可 let resultado = await axios.put(presignedS3Url, pdf, { headers: { 'Content-Type': 'application/pdf' } }); return resultado.statusText; } catch (error) { console.log("PDF上传失败:", error) throw error; } }
额外排查项
如果抛出的错误是chrome启动相关,优先检查:
- Lambda运行内存是否至少配置为1024M,内存低于512M时chrome-aws-lambda大概率启动失败
- chrome-aws-lambda和puppeteer-core版本是否匹配,部署包是否正确包含chrome二进制文件
- Lambda执行角色是否有临时目录读写权限(chrome启动时需要在/tmp目录写临时文件)
内容的提问来源于stack exchange,提问作者Acaedric
相关产品推荐
相关产品推荐

