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

能否为Firebase Storage动态路径编写onFinalize云函数触发器并合并PDF?

实现方案:Firebase Cloud Function 监听Storage上传并合并PDF

当然可以实现你的需求!下面是一步步的具体实现方案,完全适配你的场景:

一、创建监听任意loads子文件夹的Cloud Function

首先,我们可以通过通配符路径来监听loads下所有子文件夹的文件上传事件,不管具体的loadID是什么。触发器路径设置为loads/{loadId}/{fileName},这样任何上传到loads/xxx/下的文件都会触发onFinalize事件。

1. 初始化Cloud Function项目(如果还没做)

如果是第一次创建Cloud Function,先在本地初始化:

firebase init functions

选择JavaScript/TypeScript,根据提示完成初始化。

2. 安装依赖

我们需要用到pdf-lib来合并PDF,还有Firebase相关的库:

cd functions
npm install pdf-lib firebase-admin @google-cloud/storage

二、编写核心函数逻辑

下面是完整的Cloud Function代码,包含触发监听、文件检测、PDF合并和上传的全流程:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const { PDFDocument } = require("pdf-lib");
const { Storage } = require("@google-cloud/storage");

admin.initializeApp();
const storage = new Storage();

exports.mergePDFOnUpload = functions.storage.object().onFinalize(async (object) => {
  // 获取上传文件的基本信息
  const filePath = object.name;
  const bucketName = object.bucket;
  const loadIdMatch = filePath.match(/loads\/([^\/]+)\/([^\/]+)\.pdf/);
  
  // 验证路径格式是否符合预期
  if (!loadIdMatch) {
    functions.logger.info("文件路径不符合格式,跳过处理");
    return null;
  }

  const loadId = loadIdMatch[1];
  const uploadedFileName = loadIdMatch[2];
  const bucket = storage.bucket(bucketName);
  
  // 定义需要的两个PDF文件名
  const requiredFiles = ["confirmationOrder", "POD"];
  const missingFile = requiredFiles.find(file => file !== uploadedFileName);

  // 如果上传的不是我们需要的两个文件之一,直接返回
  if (!missingFile) {
    functions.logger.info("上传的文件不是目标PDF,跳过处理");
    return null;
  }

  // 检查另一个文件是否存在
  const otherFileRef = bucket.file(`loads/${loadId}/${missingFile}.pdf`);
  const [otherFileExists] = await otherFileRef.exists();

  if (!otherFileExists) {
    functions.logger.info(`缺少${missingFile}.pdf,暂不合并`);
    return null;
  }

  // 下载两个PDF文件的Buffer
  const [uploadedFileBuffer] = await bucket.file(filePath).download();
  const [otherFileBuffer] = await otherFileRef.download();

  // 合并PDF
  const mergedPdf = await PDFDocument.create();
  const uploadedPdf = await PDFDocument.load(uploadedFileBuffer);
  const otherPdf = await PDFDocument.load(otherFileBuffer);

  // 复制两个PDF的所有页面到合并文档
  const uploadedPages = await mergedPdf.copyPages(uploadedPdf, uploadedPdf.getPageIndices());
  uploadedPages.forEach(page => mergedPdf.addPage(page));
  
  const otherPages = await mergedPdf.copyPages(otherPdf, otherPdf.getPageIndices());
  otherPages.forEach(page => mergedPdf.addPage(page));

  // 生成合并后的PDF Buffer
  const mergedPdfBuffer = await mergedPdf.save();

  // 上传合并后的文件到同一文件夹
  const mergedFileName = `loads/${loadId}/combined_order_pod.pdf`;
  const mergedFileRef = bucket.file(mergedFileName);

  // 检查合并后的文件是否已存在,避免重复上传
  const [mergedFileExists] = await mergedFileRef.exists();
  if (mergedFileExists) {
    functions.logger.info("合并后的PDF已存在,跳过上传");
    return null;
  }

  await mergedFileRef.save(mergedPdfBuffer, {
    contentType: "application/pdf",
    metadata: {
      contentType: "application/pdf"
    }
  });

  functions.logger.info(`成功合并PDF并上传至${mergedFileName}`);
  return null;
});

三、关键细节说明

  • 路径匹配:通过正则表达式/loads\/([^\/]+)\/([^\/]+)\.pdf/提取loadID和上传的文件名,确保只处理我们需要的PDF文件。
  • 存在性检查:每次上传文件时,都会检查另一个必需的PDF是否存在,只有两个都存在时才会执行合并。
  • 避免重复合并:上传合并后的文件前,先检查是否已存在,防止同一文件夹下多次触发合并操作。
  • 权限配置:确保Cloud Function的服务账号拥有Firebase Storage的读写权限(默认初始化的服务账号通常已经具备,但如果遇到权限问题,可以在Firebase控制台的IAM页面调整)。

四、与你的前端代码适配

你的前端上传代码完全没问题,上传路径loads/${loadID}/${fileName}.pdf正好匹配我们的触发器路径,不需要做任何修改。当用户上传POD.pdf时,如果该文件夹已经有confirmationOrder.pdf,就会自动触发合并;反之,如果先上传POD.pdf,等后续上传confirmationOrder.pdf时也会触发合并。

五、测试与部署

  • 本地测试:可以使用Firebase模拟器来本地测试函数逻辑:
    firebase emulators:start
    
    上传测试文件到模拟器的Storage,查看函数日志是否正常执行。
  • 部署到生产环境:测试通过后,部署函数:
    firebase deploy --only functions:mergePDFOnUpload
    

内容的提问来源于stack exchange,提问作者nickstojanovic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 13:22:40