Firebase Cloud Functions - 创建 pdf、存储到存储桶并通过邮件发送

2024-03-28

我正在开发一个 Firebase 函数,当新订单添加到实时数据库时会触发该函数。它做的第一件事是创建一个 pdf 并将其通过管道传输到谷歌云存储桶。

在存储桶流的 .on("finish") 事件上,下一个函数将启动,该函数应通过电子邮件将管道传输的 pdf 发送给客户。

一切似乎都有效,至少有一点。

首先我遇到了一个问题,附加的 pdf 总是空的。 (不只是空白。我还在记事本++中打开它,它确实是空的)。当我检查bucketFileStream.on("finished")函数内的doc和bucketFileSream变量时,长度均为0。在doc.end之后直接检查doc变量显示长度约为612。

然后我更改了流程,在 sendOrderEmail 函数中,我还从存储桶中新创建的文件中打开一个新的读取流。

现在我至少在附件中获得了 PDF 的一些内容,但从未获得全部内容。

当我检查上传到存储桶的 PDF 时,它看起来应该如此。

我用谷歌搜索了很多,找到了一些也针对这个主题的答案,但正如在这些问题的评论中看到的那样,它们并不完全有帮助。

  • PDF 附件 NodeMailer https://stackoverflow.com/questions/37232326/pdf-attachment-nodemailer
  • 在哪里生成 Firebase 数据库数据的 PDF - 移动应用程序或 Firebase 托管 Web 应用程序 https://stackoverflow.com/questions/42378751/where-to-generate-a-pdf-of-firebase-database-data-mobile-app-or-firebase-host
  • 如何使用 nodemailer 将文件附加到电子邮件 https://stackoverflow.com/questions/21934667/how-to-attach-file-to-an-email-with-nodemailer

我还检查了nodemailer文档如何正确传递附件并按照文档实现它。但没有成功。

我认为邮件是在读取流完成之前发送的。

这是我使用的包版本:

  • “@google-cloud/storage”:“1.5.2”
  • "@types/pdfkit": "^0.7.35",
  • “firebase-admin”:“5.8.0”,
  • “firebase-functions”:“^0.7.3”
  • “nodemailer”:“4.4.1”,

谁能告诉我我做错了什么,或者为这个用例提供一个使用当前软件包版本的工作示例?

这是让我发疯的代码......

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const nodemailer = require("nodemailer");
const pdfkit = require("pdfkit");
const storage = require("@google-cloud/storage")({projectId: `${PROJECT_ID}`})

const mailTransport = nodemailer.createTransport({
  host: "smtp.office365.com",
  port: 587,
  secureConnection: false,
  auth: {
    user: "userName",
    pass: "userPassword"
 },
 tls: {
   ciphers: "SSLv3",
 }
});

exports.added = function(event) {
  const order = event.data.val();
  const userId = event.params.userId;

  // Load User Data by userId
  return admin
       .database()
       .ref("/users/" +userId)
       .once("value")
       .then(function (snapshot) {
               return generateOrderPDF(snapshot.val(), userId);
       });
};

function generateOrderPDF(user, userId) {
  const doc = new pdfkit();
  const bucket = storage.bucket(functions.config().bucket);
  const filename =  `/${userId}/test-` + Date.now() + ".pdf";
  const file = bucket.file(filename);
  const bucketFileStream = file.createWriteStream();

  // Pipe its output to the bucket
  doc.pipe(bucketFileStream);

 // Do creation Stuff....

  doc.end();

  bucketFileStream.on("finish", function () {
    return sendOrderEmail(user, filename);
  });

  bucketFileStream.on("error", function(err) {
    console.error(err);
  });
}

function sendOrderEmail(user, filename) {
  const email = user.email;
  const firstname = user.firstName;
  const mailOptions = {
    from: "[email protected] /cdn-cgi/l/email-protection",
    to: email,
    subject: "Order"
  };

  const bucket = storage.bucket(functions.config().bucket);
  const file = bucket.file(filename);

  mailOptions.html = mailTemplate;
  mailOptions.attachments =  [{
    filename: "test.pdf",
    content: file.createReadStream()
  }];

  return mailTransport.sendMail(mailOptions).then(() => {
    console.log("New order email sent to:", email);
  }).catch(error => {
    console.error(error);
  });
}

我的方法中的问题是在 pdfkit 库内部,而不是在 nodemailer 或 firebase 内部。下面的几行似乎触发了结束事件。所以 pdf 是在这些行之后发送的。在对他们发表评论后,一切都按预期进行。并不是像哈里提到的那样,从未达到终点。

/*  doc.lineCap("underline")
    .moveTo(72, 321)
    .lineTo(570, 321)
    .stroke();*/

完成 MVP 后,我将进行根本原因分析,并将最终答案发布为该答案下方的评论。

这是此用例的源代码的工作示例。它还确保 firebase 函数在所有工作完成之前不会完成。这是通过将事件驱动的 doc.on() 函数包装到承诺中来处理的,该承诺在调用 doc.on("end") 时得到解决。

exports.added = function(event) {
  const order = event.data.val();
  const userId = event.params.userId;

  // Load User Data by userId
  return admin.database().ref("/users/" + userId).once("value").then(function (snapshot) {
    return generatePDF(snapshot.val(), userId);
  });
};

function generatePDF(user, userId) {
  const doc = new pdfkit();
  const bucket = admin.storage().bucket(functions.config().moost.orderbucket);
  const filename =  "/${userId}/attachement.pdf";
  const file = bucket.file(filename);
  const bucketFileStream = file.createWriteStream();
  var buffers = [];
  let p = new Promise((resolve, reject) => {
    doc.on("end", function() {
      resolve(buffers);
    });
    doc.on("error", function () {
      reject();
    });
  });

  doc.pipe(bucketFileStream);
  doc.on('data', buffers.push.bind(buffers));

  //Add Document Text and stuff

  doc.end();

  return p.then(function(buffers) {
     return sendMail(buffers);
  });
}

function sendMail(buffers) {
  const pdfData = Buffer.concat(buffers);
  const mailOptions = {
    from: "FromName <[email protected] /cdn-cgi/l/email-protection>",
    to: "[email protected] /cdn-cgi/l/email-protection",
    subject: "Subject",
    html: mailTemplate,
    attachments: [{
      filename: 'attachment.pdf',
      content: pdfData
    }]
  };

  return mailTransport.sendMail(mailOptions).then(() => {
    console.log("New email sent to:", "[email protected] /cdn-cgi/l/email-protection");
  }).catch(error => {
    console.error(error);
  });
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Firebase Cloud Functions - 创建 pdf、存储到存储桶并通过邮件发送 的相关文章

随机推荐

  • 线程中的 GLib GMainContext?

    我在堆栈溢出上搜索以找到与我的问题相关的答案 但我没有找到任何答案 我有一个启动线程的主线程 我的 main 函数 新线程运行 GMainLoop 在我的主要功能中 我不断通过调用添加源g io watch一些文件描述符 但如果事件被调度
  • Python 日志记录和子进程输出以及错误流

    我想启动一个 python 进程并将子进程错误消息记录到父脚本的日志记录对象中 理想情况下 我希望将日志流统一到一个文件中 我可以以某种方式访问 日志记录类的输出流吗 我知道的一种解决方案是使用 proc log 进行日志记录 正如下面的答
  • 如何每天在特定时间触发 akka 调度程序?

    我创建了一个 Akka 的调度程序 每天在固定时间发送邮件 例如每天上午 6 00 那么如何称呼演员呢 我的意思是我应该使用什么逻辑 谢谢 只需计算现在和接下来的下午 6 点之间的差异 将其作为初始延迟 然后每 24 小时重复一次
  • Rails 5 - 在编辑操作中使用范围来查找特定实例的相关子项

    我正在尝试学习如何在 Rails 5 应用程序中使用范围 我问了一个背景问题here https stackoverflow com questions 41755032 rails 5 exclude specific instances
  • 在 React 中专注于 div,无需单击即可在模块上启用键盘导航

    我正在 React 中从头开始编写一个图像库 当单击图像时 会弹出一个模式 与我的库组件分开的组件 我想要使用左右箭头在图片之间导航 不仅仅是屏幕上添加的箭头 onclick 但目前它只关注当我单击一次时的模式 然后我也可以使用键盘导航 o
  • AWS - cfn-init 不创建文件

    我是云信息新手 我正在使用 cfn init 创建文件 但不会创建文件 我的堆栈也不会失败 使用 EC2 实例等所需资源成功创建堆栈 它还会安装 AWS CLI 如用户数据中所述 但它只是不创建我希望创建的文件 我尝试使用不允许回滚堆栈的高
  • 测试 RESTful 服务的方法? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我想直接通过 HTTP 测试我的 RESTful 应用程序 并且我正在寻找可以帮助我完成该任务的工具 基本上我正在寻找一个简单的 HTTP 请求包装器
  • 我是否需要告诉 Apache Tomcat 保持 Websocket 连接打开?

    我正在尝试Apache Tomcat Websocket 实现 http tomcat apache org tomcat 7 0 doc web socket howto html 问题是 连接总是在最多 30 秒的空闲时间后关闭 我是否
  • 通过 DataMapper 来自 SQLite 内存数据库的 No Such Table 错误

    我有一个 Ruby 程序 它使用 DataMapper 作为 ORM 与内存中的 SQLite DB 进行通信 这一直工作得很好 但是我最近刚刚添加了一个新的 DM 类和相应的表 令我惊讶的是 现在在 auto migrate 过程中事情发
  • 内存保护的最新技术水平如何?

    我对 C 语言 指针和内存管理等低级语言了解得越多 我就越想了解现代操作系统和内存保护的最新技术水平 例如 采取了哪些检查来防止某些恶意程序随机尝试读取尽可能多的地址空间并忽略操作系统设置的规则 一般来说 这些内存保护方案是如何工作的 他们
  • C# Microsoft Graph - 如何从 msal-browser 发送带有访问令牌的电子邮件

    我正在使用 C Microsoft Graph 发送电子邮件 但当我调用等待 graphClient Me SendMail message Request PostAsync 时 我遇到错误 对象引用未设置到对象的实例 方法 我尝试首先调
  • 如何找到ZedGraph上的索引位置

    有没有办法根据当前的 xPosition 找到曲线的索引位置 假设我有一个曲线项目 MyCurve 它有 20k 点 当鼠标移动时我可以获得鼠标位置 然后只需使用以下函数即可获得 x 和 y 位置 double xPos 0 yPos 0
  • 错误 lnk2005 已在 .obj 中定义

    关于这个错误有很多疑问 但它们只与一个变量相关 test h namespace World enum Objects TERRAIN 1 BOX 2 SPHERE 4 CAPSULE 8 void WorldObjects2 unsign
  • 如何将 SQL 结果集限制为不太常见的项目

    问题 我有一份姓名和地址列表 有些姓名 人 与其他姓名 人 具有相同的地址 街道 邮政编码 城镇 我想选择所有这些名称 其地址出现次数不超过三次 并从其余名称中选择前三个名称 每个名称都指向同一地址 例子 Albert Adr1 Berta
  • 连续单子转变

    在尝试为 ContT monad 转换器建立一些直觉时 我 也许并不奇怪 发现自己很困惑 问题在于 shiftT 操作似乎没有做任何有用的事情 首先是一个如何使用它的简单示例 shiftT famr gt lift do a lt calc
  • 按月分组获取数据集的百分位数

    我有一个 SQL 表 其中包含大量记录 如下所示 Date Score 01 01 2010 4 02 01 2010 6 03 01 2010 10 16 03 2010 2 我将其绘制在图表上 因此我在图表上画了一条漂亮的线 表示随时间
  • 如何使用thymeleaf作为模板引擎和飞碟作为渲染器使用密码保护pdf报告

    PDF 已成功生成 但我想用密码保护它 Flying saucer pdf 文档对我没有帮助 我正在使用这个例子使用thymeleaf flying saucer pdf Spring Boot http www oodlestechnol
  • 使用把手引用 css 和 js 文件的正确方法是什么?

    我目前在我的项目中使用 Express 和车把 这是我第一次使用车把 我不知道如何正确引用我的 css 和 js 文件的位置 我当前的项目结构如下 test root views js some JS files css some css
  • Json.net 反序列化返回一个空对象

    我正在使用下面的代码进行序列化 var json JsonConvert SerializeObject new summary summary summary是类型的自定义对象SplunkDataModel public class Sp
  • Firebase Cloud Functions - 创建 pdf、存储到存储桶并通过邮件发送

    我正在开发一个 Firebase 函数 当新订单添加到实时数据库时会触发该函数 它做的第一件事是创建一个 pdf 并将其通过管道传输到谷歌云存储桶 在存储桶流的 on finish 事件上 下一个函数将启动 该函数应通过电子邮件将管道传输的