使用 nodemailer 将内联图像移动到附件

Move inline images to attachments with nodemailer

我有一个节点应用程序使用 nodemailer 成功发送电子邮件。我正在发送包含类似代码的电子邮件:

const emailRequest = {
  from: { name: "Sender name", address: "sender@example.com" },
  to: [/* recipients */],
  subject: "Email subject",
  text: "Some email text",
  html: '<p>Some email text</p><p><img src="data:image/png;base64,{base64_image_content}">',
} as SendMailOptions;

const ses = new SES();
const transporter = nodemailer.createTransport({
  SES: ses,
  sendingRate: MAX_SEND_RATE,
});

transporter.sendMail(emailRequest, sendMailResponseHandler);

问题是有时电子邮件正文包含格式如下的内联图像,其中 {base64_image_content} 是包含图像的实际数据:

<img src="data:image/png;base64,{base64_image_content}">

Gmail does not support inline image data。当我在 Gmail 中选择“显示原件”时,电子邮件发送并显示图像数据,但图像在 Gmail 网络客户端中不显示,在 Gmail 移动客户端中显示为损坏的图像。

我无法控制内联图像,所以我希望找到一种方法来提取内联图像数据并将其作为附件添加到电子邮件,并在调用 transporter.sendMail.

之前使用 cid 来引用电子邮件正文中的那些附件

我想将 HTML 更改为:

<p>Some email text</p><p><img src="data:image/png;base64,{base64_image_content}">

对此:

<p>Some email text</p><p><img src="cid:myImageCid">

并将图像添加为附件:

const emailRequest = {
  // other things
  attachments: [{ cid: "myImageCid", content: base64_image_content }]
} as SendMailOptions;

我需要帮助的部分是从原始电子邮件中提取图像数据 HTML 并将其替换为 cid 参考。有什么办法可以做到吗?

像这样在邮件中发送附件您可以在请求中提供附件。

 const emailRequest = {
      from: { name: "Sender name", address: "sender@example.com" },
      to: [/* recipients */],
      subject: "Email subject",
      attachment: [{
         path: filePath, //"public/data/somefile.png"
         filename: filename,
         cid: filename + "@"
      }],
      text: "Email text",
      html: "Email HTML",
    } as SendMailOptions;
require("fs").writeFile("out.png", (some_email_html.split("img")[1]).replace('">',"").split(",")[1], 'base64', function(err) {

console.log(err); 

});

上面的代码将从html中提取一张图片,假设some_email_html作为邮件正文,并将其保存为out.png,您可以将名称更改为任何其他名称, 并在电子邮件发送后删除。请注意,代码假定您需要一张图片,您可以修改以提取更多图片,或者如果您也需要帮助,请告诉我。

我最终听从了 akisoft 的建议,自己提取了图像数据。这是我的做法:

extractImages = (html: string) => {
  // Match all image tags with data in their src
  const regex = /<img src="data:.+?">/gs;
  const images = html.match(regex) ?? [];
  const attachments = images.map((originalImageTag) => {
    // Parse out the image data and meta info
    const imageSrc = (originalImageTag.match(/<img src="data:(.+?)">/s) ?? [])[1];
    const [imageMeta, content] = imageSrc.split(",");
    const [imageType, encoding] = imageMeta.split(";");
    const fileType = imageType.split("/")[1];
    const filename = `image.${fileType}`;
    const cid = uuid();
    const newImageTag = `<img src="cid:${cid}">`;

    // Replace the image tags with ones that reference attached content
    html = html.replace(originalImageTag, newImageTag);

    // Collect attachment data to be passed to nodemailer
    return {
      filename,
      cid,
      encoding,
      content,
      contentDisposition: "inline",
    } as Attachment;
  });

  // Return the modified HTML and the attachments
  return { html, attachments };
};

我将原始电子邮件的 HTML 传递给该函数,它 returns 修改了 HTML 和一个附件列表,我可以直接传递给 nodemailer。