You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 15:17:27