企业级邮件发送:AWS SESv2 + S3 + Raw Email 高级实践
在现代企业应用中,发送高质量邮件不仅仅是发送文本,而是需要 HTML 模板、多语言支持、附件、内嵌图片 等功能。本文将介绍如何使用 AWS SESv2 + S3 + Raw Email 构建企业级邮件发送方案。
目录
背景与挑战
企业邮件发送通常面临以下问题:
- HTML 模板复杂,含表格、CSS、MJML 注释
- 占位符需要动态替换
- 邮件客户端兼容性差(Gmail、Outlook、Apple Mail)
- 附件 PDF 或图片需要安全发送
- SES 默认
sendEmailAPI 不支持完整控制 MIME 结构
解决方案:使用 SESv2 + Raw Email + Base64 编码,结合 S3 存储模板或附件,实现企业级邮件发送。
技术方案概览
整体流程如下:
-
模板存储
- HTML 模板或 MJML 文件存储在 S3
- 模板中包含占位符,如
{{client_email}}、{{invoice_id}}
-
模板渲染
- 使用 MJML → Handlebars
- 填充占位符生成 HTML 内容
-
Raw Email 构建
- 使用 multipart/alternative 包含 text/plain 与 text/html
- 使用 Content-Transfer-Encoding: base64 避免 HTML 被破坏
-
发送邮件
- SESv2
sendRawEmailAPI - 可添加附件(multipart/mixed)或内嵌图片
- SESv2
HTML 模板与变量渲染
示例 MJML 模板
<mjml>
<mj-body>
<mj-section>
<mj-column>
<mj-text font-size="16px" font-weight="bold">
Dear {{client_email}},
</mj-text>
<mj-text>
Please find attached invoice <b>#{{invoice_id}}</b> for service on <b>{{date}}</b>.
</mj-text>
</mj-column>
</mj-section>
</mj-body>
</mjml>
使用 Handlebars 渲染变量
import mjml2html from "mjml";
import Handlebars from "handlebars";
const mjmlTemplate = /* 从 S3 获取模板内容 */;
const html = mjml2html(mjmlTemplate).html;
const data = {
client_email: "alice@example.com",
invoice_id: "12345",
date: "2025-11-20"
};
const htmlContent = Handlebars.compile(html)(data);
✅ 确保 HTML 中的占位符被替换,否则邮箱客户端可能显示为空白。
构建 Raw Email
SES sendRawEmail 需要完整 MIME 结构:
const boundary = "----=_Part_Boundary_123456";
const rawEmail = [
`From: sender@example.com`,
`To: recipient@example.com`,
`Subject: Your Invoice`,
`MIME-Version: 1.0`,
`Content-Type: multipart/alternative; boundary="${boundary}"`,
``,
`--${boundary}`,
`Content-Type: text/plain; charset=UTF-8`,
`Content-Transfer-Encoding: base64`,
``,
Buffer.from(textContent).toString("base64"),
`--${boundary}`,
`Content-Type: text/html; charset=UTF-8`,
`Content-Transfer-Encoding: base64`,
``,
Buffer.from(htmlContent).toString("base64"),
`--${boundary}--`,
].join("\r\n");
注意:使用 base64 编码可避免 HTML 被破坏。
附件与图片内嵌处理
- PDF 或 Excel 附件:使用
multipart/mixed,每个附件都用 Base64 编码 - 内嵌图片:使用
Content-ID引用<img src="cid:xxx">
示例:
--MIXED_BOUNDARY
Content-Type: application/pdf; name="invoice.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="invoice.pdf"
<base64 PDF 内容>
--MIXED_BOUNDARY--
完整 Node.js 示例
import { SESv2Client, SendEmailCommand } from "@aws-sdk/client-sesv2";
import mjml2html from "mjml";
import Handlebars from "handlebars";
const mjmlTemplate = `...`; // 从 S3 获取
const data = { client_email: "alice@example.com", invoice_id: "12345", date: "2025-11-20" };
const htmlContent = Handlebars.compile(mjml2html(mjmlTemplate).html)(data);
const textContent = `Dear ${data.client_email}, Invoice #${data.invoice_id} on ${data.date}\nKind Regards,\neSIMfx Team`;
const boundary = "----=_Part_Boundary_123456";
const rawEmail = [
`From: sender@example.com`,
`To: recipient@example.com`,
`Subject: Your Invoice`,
`MIME-Version: 1.0`,
`Content-Type: multipart/alternative; boundary="${boundary}"`,
"",
`--${boundary}`,
`Content-Type: text/plain; charset=UTF-8`,
`Content-Transfer-Encoding: base64`,
"",
Buffer.from(textContent).toString("base64"),
`--${boundary}`,
`Content-Type: text/html; charset=UTF-8`,
`Content-Transfer-Encoding: base64`,
"",
Buffer.from(htmlContent).toString("base64"),
`--${boundary}--`,
].join("\r\n");
const client = new SESv2Client({ region: "us-east-1" });
await client.send(
new SendEmailCommand({
FromEmailAddress: "sender@example.com",
Destination: { ToAddresses: ["recipient@example.com"] },
Content: { Raw: { Data: Buffer.from(rawEmail) } },
})
);
console.log("Email sent successfully!");
最佳实践与注意事项
- Base64 编码 HTML 避免邮件客户端解析失败
- 使用 multipart/alternative 支持纯文本 fallback
- 占位符必须渲染完毕,否则空白邮件
- 附件也用 Base64,保证传输安全
- MJML → HTML → Handlebars,保持模板可维护性
总结
使用 SESv2 + S3 + Raw Email 结合 MJML + Handlebars + Base64 可以实现:
- 企业级 HTML 邮件发送
- 多占位符动态渲染
- 附件与图片内嵌
- 跨客户端兼容(Gmail/Outlook/Apple Mail)
这是企业级邮件发送的标准做法,既安全又可扩展。