我正在开发Firebase功能,当新订单添加到实时数据库时会触发该功能。它首先要做的是创建一个pdf并将其传输到谷歌云存储桶。
在桶流的.on(“完成”)事件中,下一个函数启动,应该通过电子邮件将管道pdf发送给客户。
一切似乎都有效,至少有一点。
首先我遇到了问题,附加的pdf总是空的。 (不只是空白。我也用记事本++打开它,它真的是空的)。当我在bucketFileStream.on(“finished”)函数中检查doc和bucketFileSream变量时,两者的长度都为0.在doc.end之后直接检查doc var会显示612的长度。
然后我改变了流程,在sendOrderEmail函数中我也从桶中新创建的文件中打开一个新的读取流。
现在我在附件中至少得到了一些PDF文件,但从来没有全部内容。
当我检查上传到存储桶的PDF时,它看起来应该是。
我搜索了很多,并找到了一些针对这个主题的答案,但正如在这些问题的评论中所看到的那样,它们并没有完全有用。
我还使用nodemailer文档检查了如何正确传递附件并按照文档实现它。但没有成功。
我认为邮件是在读取流完成之前发送的。
这里是我使用的包版本:
任何人都可以告诉我我做错了什么或者为这个用例提供了一个使用当前软件包版本的工作示例吗?
这是让我疯狂的代码......
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);
});
}
答案 0 :(得分:3)
代码中的主要问题是stream.on('finish')永远不会完成。我也遇到了同样的问题。
而不是流式传输,将pdf转换为缓冲区并将其作为附件发送。
以下对我来说很好,
const doc = new pdfkit()
const filename = '/${userId}/test-' + Date.now() + ".pdf"
const file = bucket.file(filename);
const bucketFileStream = file.createWriteStream();
doc.pipe(bucketFileStream);
doc.end();
var buffers = []
doc.on('data', buffers.push.bind(buffers));
doc.on('end',function(){
let pdfData = Buffer.concat(buffers);
'<<nodemailer stuffs goes here>
'attach the doc as content
});
答案 1 :(得分:3)
我的appraoch中的问题是在pdfkit库中,而不是在nodemailer或firebase中。下面的行似乎触发了结束事件。所以在这些行之后发送了pdf。在评论之后,一切都按预期工作。并不像Hari所提到的那样完成任务。
/* doc.lineCap("underline")
.moveTo(72, 321)
.lineTo(570, 321)
.stroke();*/
在完成MVP之后,我将进行根本原因分析,并在最终答案后发表评论。
这是此UseCase的源代码的工作示例。它还确保在完成所有工作之前,firebase功能无法完成。这是通过将事件驱动的doc.on()函数包装到promise中来处理的,该函数在调用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 <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);
});
}