Firebase云函数中使用pdf-lib合并PDF遇TypeError问题求助
解决pdf-lib在Firebase云函数中出现的TypeError: Cannot read property 'node' of undefined问题
看起来你遇到的问题主要来自两个核心点:getSignedUrl的返回值格式和异步函数的调用处理,我来帮你一步步梳理并修正:
问题1:getSignedUrl返回的是数组而非字符串
getSignedUrl方法返回的Promise,resolve后的值是包含签名URL的数组(比如['https://your-signed-url']),但你直接把results[0]、results[1]传给copyPages,相当于把数组传给了fetch,这会导致获取到的不是正确的PDF字节数据,最终让PDFDocument.load失败,触发后续的node属性未定义错误。
问题2:异步函数调用未处理Promise
在listFilesByPrefix().then(...)里调用copyPages这个async函数,但没有用await或者.then()处理它的Promise,可能会导致未捕获的异步错误,无法在日志中看到完整的错误栈。
修正后的完整代码
const functions = require('firebase-functions'); const { Storage } = require('@google-cloud/storage'); const storage = new Storage(); const admin = require('firebase-admin'); admin.initializeApp(); const { PDFDocument } = require('pdf-lib'); const fetch = require('node-fetch'); exports.testCloudFunc = functions.storage.object().onFinalize(async object => { const filePath = object.name; console.log(`Logged: FILEPATH: ${filePath}`); const id = filePath.split('/'); console.log(`Logged: ID: ${id[0]}/${id[1]}`); const bucket = object.bucket; console.log(`Logged: BUCKET: ${object.bucket}`); async function listFilesByPrefix() { const options = { prefix: `${id[0]}/${id[1]}` }; const [files] = await storage.bucket(bucket).getFiles(options); // 简化文件过滤逻辑,更易维护 const targetFilenames = ['invoiceReport.pdf', 'POD.pdf', 'rateConfirmation.pdf']; const endFiles = files.filter(el => targetFilenames.includes(el.name.split('/').pop())); endFiles.forEach(el => console.log(el.name)); // 提取签名URL数组中的字符串值 const urlsArray = await Promise.all( endFiles.map(file => file.getSignedUrl({ action: 'read', expires: '03-17-2025' }) .then(urls => urls[0]) // 取出数组里的真实URL ) ); return urlsArray; } try { const results = await listFilesByPrefix(); results.forEach(el => console.log(el)); // 确保至少有2个PDF文件再执行合并 if (results.length >= 2) { await copyPages(results[0], results[1]); console.log('PDF合并操作完成'); } else { console.log('需要至少2个目标PDF文件才能执行合并'); } } catch (err) { console.error('整体处理流程出错:', err); } }); async function copyPages(url1, url2) { try { // 转换为Buffer格式(pdf-lib兼容Buffer和ArrayBuffer) const firstDonorPdfBytes = await fetch(url1).then(res => res.buffer()); const secondDonorPdfBytes = await fetch(url2).then(res => res.buffer()); const firstDonorPdfDoc = await PDFDocument.load(firstDonorPdfBytes); const secondDonorPdfDoc = await PDFDocument.load(secondDonorPdfBytes); // 提前检查页面索引是否存在,避免越界错误 if (secondDonorPdfDoc.getPageCount() <= 742) { throw new Error('第二个PDF没有第743页(页面索引从0开始计数)'); } const pdfDoc = await PDFDocument.create(); const [firstDonorPage] = await pdfDoc.copyPages(firstDonorPdfDoc, [0]); const [secondDonorPage] = await pdfDoc.copyPages(secondDonorPdfDoc, [742]); pdfDoc.addPage(firstDonorPage); pdfDoc.insertPage(0, secondDonorPage); const pdfBytes = await pdfDoc.save(); // 可选:将合并后的PDF上传回Firebase存储桶 // await storage.bucket(bucket).file(`${id[0]}/${id[1]}/merged.pdf`).save(pdfBytes); } catch (err) { console.error('PDF合并过程出错:', err); throw err; // 向上抛出错误,让外层try/catch捕获 } }
额外优化说明
- 全局错误捕获:添加了多层try/catch块,能更清晰地定位错误发生的阶段
- 页面索引校验:避免因为目标PDF页数不足导致的额外错误
- 文件过滤逻辑简化:用数组
includes方法替代冗余的条件判断,后续新增文件名更方便 - 合并后存储提示:注释了将合并结果上传回存储桶的示例代码,你可以根据需求开启
内容的提问来源于stack exchange,提问作者Madcap
相关产品推荐
相关产品推荐

