Firebase Cloud Functions-创建pdf,存储到存储桶并通过邮件发送 [英] Firebase Cloud Functions - create pdf, store to bucket and send via mail

查看:42
本文介绍了Firebase Cloud Functions-创建pdf,存储到存储桶并通过邮件发送的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm developing a Firebase Function, which is triggered when a new order is added to the Realtime Database. The first thing it does is to creat a pdf and pipe it to a google cloud storage bucket.

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

On the .on("finish") event of the bucket stream, the next function gets started, which should send the piped pdf via email to the customer.

一切似乎都正常,至少有一点.

Everything seems to work, at least a bit.

首先,我遇到了一个问题,即随附的pdf始终为空.(不仅是空白.我还用记事本++打开了它,实际上它全是空的).当我检查bucketFileStream.on("finished")函数内的doc和bucketFileSream变量的长度均为0时.直接在doc.end之后检查doc var的长度为612.

First I had the problem, that the attached pdf always was empty. (Not just blank. I also opened it in notepad++ and it really was all empty). When I checked the doc and bucketFileSream vars inside the bucketFileStream.on("finished") function both had a length of 0. A check of the doc var directly after doc.end showed a length of somewhat 612.

然后,我更改了流程,即在sendOrderEmail函数中,我还从存储桶中新创建的File中打开了一个新的Read Stream.

I then changed the flow, that in the sendOrderEmail function I also open a new Read Stream from the newly created File in the bucket.

现在我至少在附件中得到了PDF的一些内容,但没有得到全部内容.

Now I get at least some stuff of the PDF in the attachement, but never the whole content.

当我检查上传到存储桶的PDF时,看起来应该是应该的.

When I check the PDF uploaded to the bucket, it looks like it should.

我在Google上搜索了很多,找到了一些也针对该主题的答案,但是从对这些问题的评论中也可以看出,它们并没有完全有用.

I googled alot and found some answers that were also targeting this topic, but as also seen in comments on these questions, they were not completly helpful.

我还与nodemailer文档一起检查了如何正确传递附件,并按照文档中的说明进行了实现.但是没有成功.

I also checked with the nodemailer documentation how to pass the attachement correctly and implemented it as documented. But no success.

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

I think that the mail gets sent before the Read Stream has finished.

这是我使用的软件包版本:

Here the Package Versions I use:

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

对于这个用例,有人可以告诉我我做错了什么吗,或者提供一个使用当前软件包版本的有效示例吗?

Can anyone tell me what I'm doing wrong or provide a working example, which uses current package versions, for this usecase?

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

Here is the code which drives me crazy...

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: "test@test.test",
    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.在对他们发表评论后,一切都按预期进行.并非从未像Hari提到的那样达到终点.

The problem in my appraoch was inside the pdfkit library and not inside nodemailer or firebase. The lines below seem to trigger the end event. So the pdf got sent after these lines. After out commenting them everything worked as it should. It was not that finish was never reached like Hari mentioned.

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

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

After finishing the MVP I will take a root cause analysis and post the final answer as comment below this answer.

这是此UseCase的源代码的有效示例.它还可以确保Firebase功能在完成所有工作之前不会完成.这可以通过将事件驱动的doc.on()函数包装到promise中来解决,当调用doc.on("end")时可以解决该问题.

This is a working sample of Source-Code for this UseCase. It also ensures, that the firebase function won't finish before all work is done. That is handled by wrapping the event driven doc.on() function into a promise, that is resolved when doc.on("end") is called.

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 <from@example.com>",
    to: "to@example.com",
    subject: "Subject",
    html: mailTemplate,
    attachments: [{
      filename: 'attachment.pdf',
      content: pdfData
    }]
  };

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

这篇关于Firebase Cloud Functions-创建pdf,存储到存储桶并通过邮件发送的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆