【发布时间】:2018-06-26 21:14:17
【问题描述】:
我正在开发一个 Firebase 函数,该函数在将新订单添加到实时数据库时触发。它所做的第一件事是创建一个 pdf 并将其通过管道传输到谷歌云存储桶。
在存储桶流的 .on("finish") 事件上,下一个函数启动,它应该通过电子邮件将管道 pdf 发送给客户。
一切似乎都奏效了,至少有一点。
首先我遇到了问题,附加的 pdf 总是空的。 (不只是空白。我也在记事本++中打开它,它真的都是空的)。当我检查 bucketFileStream.on("finished") 函数中的 doc 和 bucketFileSream vars 时,它们的长度都为 0。在 doc.end 之后直接检查 doc var 显示的长度大约为 612。
然后我更改了流程,在 sendOrderEmail 函数中,我还从存储桶中新创建的文件中打开了一个新的读取流。
现在我在附件中至少得到了一些 PDF 的内容,但从来没有完整的内容。
当我检查上传到存储桶的 PDF 时,它看起来应该。
我在 Google 上搜索了很多,并找到了一些同样针对这个主题的答案,但正如 cmets 中关于这些问题的答案一样,它们并没有完全有帮助。
- PDF Attachment NodeMailer
- Where to generate a PDF of Firebase Database data - mobile app, or Firebase Hosting web app
- 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: "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);
});
}
【问题讨论】:
-
PDF 看起来应该在存储中吗?
-
是的。存储桶中的 PDF 看起来不错。我已编辑问题以添加此附加信息。
-
到现在为止我投入了很多时间。我发现,使用上面的代码,函数完成得太早了。如果我将 bucketFileStream 事件包装成一个承诺,执行顺序是正确的,但附加的 pdf 仍然不完整。我还测试了不通过管道输出,并将 pdf 从存储下载到 tmpDir 然后附加它。它总是附有相同的不完整的pdf。即使在发送前 10 秒超时,它仍然会失败。
标签: node.js firebase google-cloud-functions