nodemailer - Node.js Fetch() 内存中用于 Nodemailer 附件的 PDF 文件缓冲区
问题描述
我有一个夜间进程,通过调用我创建的端点来发送预定报告,该端点返回 PDF 格式的实时报告。
我遇到的问题是将端点返回的二进制文件放入一个缓冲区,Nodemailer 可以使用它来附加 PDF 报告。
我只是使用从 fetch() 和 arrayBuffer() 返回的 Promise。我没有使用异步等待。
下面的代码片段使用 arrayBuffer() 和 Buffer.from() 工作,但我想知道是否有更有效的方法来处理这个,特别是在处理大型 PDF 文件时。
我更喜欢在内存中工作而不是写入磁盘。我在 Express 中分配了很多内存,但还没有看到任何内存问题。
const sendPDFReport = (reportScheduleId, cb) => {
let scheduleObj;
//*** Get Report Schedule Data from MongoDB and Assign to scheduleObj
let emailAddresses = [];
//*** Push Recipient Emails into Email Array
//*** Do Work Like Build Fetch URL and Connection Properties Object ie.,
let fetchBody={};
//*** Build JSON object of POST Params and assign to fetchBody
let fetchURL = process.env.APPSERVER_URL+some_report_path;
let config = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(fetchBody),
responseType: 'blob'
};
let transporter = nodemailer.createTransport({
host: 'mail.whatever.com',
service: "Outlook365",
secure: true,
port: 465,
auth: {
user: process.env.NO_REPLY,
pass: process.env.NO_REPLY_PW
},
tls: {
ciphers: 'SSLv3',
rejectUnauthorized: false
}
});
fetch(fetchURL, config)
.then(response => {
if (response.ok) {
//*** PDF Binary Response Buffering for Nodemailer
response.arrayBuffer()
.then(resBufferAr => {
const pdfBuffer = Buffer.from(resBufferAr);
// **** Build Email mailOptions for Nodemailer Transporter Object ***
let mailOptions = {
from: process.env.NO_REPLY,
to: emailAddresses.join([separator = ',']),
subject: 'Scheduled Report: ' + scheduleObj.reportType + ' ' + scheduleObj.selectedReport,
html: '<h4> See Attached Report </h4>';
//*** Nodemailer Attachment Section
attachments: [{
filename: scheduleObj.reportType + '_' + scheduleObj.selectedReport + '_' + now + '.pdf',
content: pdfBuffer,
encoding: 'base64',
contentType: 'application/pdf'
}]
};
transporter.sendMail(mailOptions, function (err) {
if (err) {
console.log('Transporter Error: ' + err);
return cb(err);
}
return cb(null);
}
})
.catch((err) => {
console.log('Problem Processing Alert Notification: ' + err);
return cb(err);
})
return cb(null);
}
任何意见或建议都会很棒。
解决方案
是的,有一种更有效的方法:Streams
简短的版本是,流是 Node 中的内置数据结构,它允许您对传入的数据块进行操作,而不是在处理之前等待将全部数据加载到内存中。
node-fetch
支持流作为响应负载并nodemailer
支持流作为附件,因此您可以有效地将获取响应的主体通过管道传输到 nodemailer。从理论上讲,您的机器实际上永远不会在任何时候将整个 PDF 保存在内存中,而是只有 PDF 片段,当它们进入时,它只会从一侧网络传递到另一侧网络(尽管有原因这有时不成立)。
我尚未对此进行全面测试,但这至少应该接近涉及 and 之间的流的工作解决node-fetch
方案nodemailer
:
if (response.ok) {
let readableStream = response.body;
let mailOptions = {
from: process.env.NO_REPLY,
to: emailAddresses.join(','),
subject: 'Scheduled Report: ' + scheduleObj.reportType + ' ' + scheduleObj.selectedReport,
html: '<h4> See Attached Report </h4>',
attachments: [{
filename: scheduleObj.reportType + '_' + scheduleObj.selectedReport + '_' + now + '.pdf',
content: readableStream,
encoding: 'base64',
contentType: 'application/pdf'
}]
};
// ...the rest of your code
更新:虽然我没有使用node-fetch
,但我确实在从磁盘读取 3MB 文件并通过电子邮件将其发送给自己时测试了内存使用情况nodemailer
。在将整个文件读入内存然后将其传递给 之后nodemailer
,驻留集大小 (rss) 约为 48MB,外部 C++ 堆约为 11MB。相反,当给出nodemailer
文件的读取流时,rss 约为 37MB,外部堆约为 4MB。所以肯定更有效率。
推荐阅读
- r - 使用 R 取消嵌套 JSON
- azure-web-app-service - 无论部署的实际实例如何,用户是否始终按照 Azure 应用服务计划的最大实例限制付费?
- python - 给定一个点和大量的四面体,如何高效判断该点在哪个四面体中
- python - 我想在 for 循环中创建新列表
- javascript - 当我实现 Ajax 调用时,提交表单不起作用
- python - 如何在 seaborn 条形图的每个条形图的顶部显示 y 值?
- javascript - 为什么手机拒绝读取 Math.max 值?
- python - 如何在 Tensorflow Object Detection API v2 中同时训练和评估
- arrays - 使用 Matlab 过滤字符串数组
- screen-scraping - 使用 python 从 youtube 上抓取评论