View a markdown version of this page

使用 Amazon SES API v2 发送原始电子邮件 - Amazon Simple Email Service

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 Amazon SES API v2 发送原始电子邮件

您可以通过内容类型指定为 raw 的 Amazon SES API v2 SendEmail 操作,使用原始电子邮件格式向收件人发送自定义消息。

关于电子邮件标头字段

简单邮件传输协议(SMTP)通过定义邮件信封及其部分参数来指定电子邮件将如何发送,但它本身与邮件内容无关。相反,Internet 邮件格式(RFC 5322)定义了如何创建邮件。

根据 Internet 邮件格式规范,每封电子邮件都包含标题和正文。标题包含邮件元数据,正文包含邮件本身。有关电子邮件标题和正文的更多信息,请参阅Amazon SES 中的电子邮件格式

使用原始电子邮件 MIME 消息构建

SMTP 协议最初设计用于发送仅包含 7 位 ASCII 字符的电子邮件。此规范使得 SMTP 不足以应对非 ASCII 文本编码(如 Unicode)、二进制内容或附件。多用途 Internet 邮件扩展标准(MIME)是为了能够使用 SMTP 发送许多其他种类的内容而制定的。

MIME 标准的工作方式是将邮件正文拆分为多个段,然后指定要对每个段执行的操作。例如,电子邮件正文的一个段可能是纯文本,另一个段可能是 HTML。此外,MIME 还允许电子邮件包含一个或多个附件。邮件收件人可以在电子邮件客户端内查看附件,也可以保存附件。

邮件标题和内容由一个空白行分隔。电子邮件的每个段都由一个边界(表示每个段的开头和末尾的一个字符串)分隔。

以下示例中的多段邮件包含文本、HTML 部分和附件。附件应放在附件标题的正下方,并且通常按照该示例中所示采用 base64 编码。

From: "Sender Name" <sender@example.com> To: recipient@example.com Subject: Customer service contact info Content-Type: multipart/mixed; boundary="a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a" --a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a Content-Type: multipart/alternative; boundary="sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a" --sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: quoted-printable Please see the attached file for a list of customers to contact. --sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a Content-Type: text/html; charset=iso-8859-1 Content-Transfer-Encoding: quoted-printable <html> <head></head> <body> <h1>Hello!</h1> <p>Please see the attached file for a list of customers to contact.</p> </body> </html> --sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a-- --a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a Content-Type: text/plain; name="customers.txt" Content-Description: customers.txt Content-Disposition: attachment;filename="customers.txt"; creation-date="Sat, 05 Aug 2017 19:35:36 GMT"; Content-Transfer-Encoding: base64 SUQsRmlyc3ROYW1lLExhc3ROYW1lLENvdW50cnkKMzQ4LEpvaG4sU3RpbGVzLENhbmFkYQo5MjM4 OSxKaWUsTGl1LENoaW5hCjczNCxTaGlybGV5LFJvZHJpZ3VleixVbml0ZWQgU3RhdGVzCjI4OTMs QW5heWEsSXllbmdhcixJbmRpYQ== --a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a--

邮件的内容类型为 multipart/mixed,这表示邮件有很多个段(在本例中为一个正文和一个附件),且接收客户端必须单独处理每个段。

还有一个嵌套在正文部分中的段,该段使用 multipart/alternative 内容类型。此内容类型表示每个段都包含同一内容的替代版本(在本例中为一个文本版本和一个 HTML 版本)。如果收件人的电子邮件客户端可以显示 HTML 内容,则它显示邮件正文的 HTML 版本。如果收件人的电子邮件客户端无法显示 HTML 内容,则它显示邮件正文的纯文本版本。

邮件的两个版本还将包含一个附件(在本例中为包含一些客户名称的短文本文件)。

当您将一个 MIME 段嵌套在另一个段中时(如本示例所示),嵌套的段必须使用与父段中的 boundary 参数不同的 boundary 参数。这些边界应该是唯一的字符串。要定义 MIME 段之间的边界,请键入两个连字符 (--) 后跟边界字符串。在 MIME 段的末尾,在边界字符串的开头和末尾处放置两个连字符。

注意

一封邮件的 MIME 部分不得超过 500 个。

MIME 编码

为了保持与旧系统的兼容性,Amazon SES 遵循 RFC 2821 中定义的 SMTP 7 位 ASCII 限制。如果要发送包含非 ASCII 字符的内容,则必须将这些字符编码为使用 7 位 ASCII 字符的格式。

电子邮件地址

电子邮件地址字符串必须为 7 位 ASCII 字符。如果您希望向或从某个地址的域部分中包含 Unicode 字符的电子邮件地址发送邮件,则必须使用 Punycode 对域进行编码。不允许在电子邮件地址的本地部分(@ 符号前面的部分)中使用 Punycode,也不允许在“易记发件人”名称中使用。如果要在“易记发件人”名称中使用 Unicode 字符,必须使用 MIME 编码字语法进行编码“易记发件人”名称,如本节所述。有关 Punycode 的更多信息,请参阅 RFC 3492

注意

此规则仅适用于您在邮件信封中指定的电子邮件地址,不适用于邮件标头。使用 Amazon SES API v2 SendEmail 操作时,您在 SourceDestinations 参数中指定的地址分别定义信封发件人和收件人。

电子邮件标头

要对邮件标头进行编码,请使用 MIME encoded-word 语法。MIME encoded-word 语法使用以下格式:

=?charset?encoding?encoded-text?=

encoding 的值可以是 QB。如果编码的值为Q,则必须使用encoded-text该值 Q-encoding。如果编码值为 B,则 encoded-text 的值必须使用 base64 编码。

例如,如果您要在电子邮件的主题行中使用字符串“Як ти поживаєш?”,那么您可以使用以下任一编码:

  • Q-encoding

    =?utf-8?Q?=D0=AF=D0=BA_=D1=82=D0=B8_=D0=BF=D0=BE=D0=B6=D0=B8=D0=B2=D0=B0=D1=94=D1=88=3F?=
  • Base64 编码

    =?utf-8?B?0K/QuiDRgtC4INC/0L7QttC40LLQsNGU0Yg/?=

有关的更多信息 Q-encoding,请参阅 RFC 204 7。有关 base64 编码的更多信息,请参阅 RFC 2045

消息正文

要对邮件正文进行编码,可以使用 quoted-printable 编码或 base64 编码。然后,使用 Content-Transfer-Encoding 标头指示您使用的编码方案。

例如,假设邮件正文包含以下文本:

१९७२ मे रे टॉमलिंसन ने पहला ई-मेल संदेश भेजा | रे टॉमलिंसन ने ही सर्वप्रथम @ चिन्ह का चयन किया और इन्ही को ईमेल का आविष्कारक माना जाता है

如果选择使用 base64 编码对此文本进行编码,请首先指定以下标头:

Content-Transfer-Encoding: base64

然后,在电子邮件的正文部分中,包含 base64 编码的文本:

4KWn4KWv4KWt4KWoIOCkruClhyDgpLDgpYcg4KSf4KWJ4KSu4KSy4KS/4KSC4KS44KSoIOCkqOCl hyDgpKrgpLngpLLgpL4g4KSILeCkruClh+CksiDgpLjgpILgpKbgpYfgpLYg4KSt4KWH4KSc4KS+ IHwg4KSw4KWHIOCkn+ClieCkruCksuCkv+CkguCkuOCkqCDgpKjgpYcg4KS54KWAIOCkuOCksOCl jeCkteCkquCljeCksOCkpeCkriBAIOCkmuCkv+CkqOCljeCkuSDgpJXgpL4g4KSa4KSv4KSoIOCk leCkv+Ckr+CkviDgpJTgpLAg4KSH4KSo4KWN4KS54KWAIOCkleCliyDgpIjgpK7gpYfgpLIg4KSV 4KS+IOCkhuCkteCkv+Ckt+CljeCkleCkvuCksOCklSDgpK7gpL7gpKjgpL4g4KSc4KS+4KSk4KS+ IOCkueCliAo=
注意

在某些情况下,您可以在使用 Amazon SES 发送的邮件中使用 8 位 Content-Transfer-Encoding。但是,如果 Amazon SES 必须对邮件进行任何更改(例如,当您使用打开和单击跟踪)时,8 位编码的内容在到达收件人的收件箱时可能无法正确显示。因此,您应始终对不是 7 位 ASCII 的内容进行编码。

文件附件

要将文件附加到电子邮件,您必须使用 base64 编码对附件进行编码。附件通常放在专用的 MIME 邮件部分中,其中包括以下标头:

  • Content-Type— 附件的文件类型。以下是常见 MIME Content-Type 声明的示例:

    • 纯文本文件Content-Type: text/plain; name="sample.txt"

    • Microsoft Word 文档Content-Type: application/msword; name="document.docx"

    • JPG 图像Content-Type: image/jpeg; name="photo.jpeg"

  • Content-Disposition— 指定收件人的电子邮件客户端应如何处理内容。对于附件,此值为 Content-Disposition: attachment

  • Content-Transfer-Encoding— 用于对附件进行编码的方案。对于文件附件,此值几乎总是 base64

  • 编码的附件:您必须对实际附件进行编码,并将其包含在附件标题下方的正文中,如示例所示

Amazon SES 接受最常见的文件类型。有关 Amazon SES 不接受的文件类型的列表,请参阅SES 不支持的附件类型

使用 Amazon SES API v2 发送原始电子邮件

Amazon SES API v2 提供 SendEmail 操作,当您将内容类型设置为简单、原始或模板化时,该操作使您可以指定的格式编写和发送电子邮件。有关的完整说明,请参阅 SendEmail。以下示例将内容类型指定为 raw,以使用原始电子邮件格式发送消息。

注意

有关对 SendEmail 进行多个调用时如何加快电子邮件发送速度的提示,请参阅增加 Amazon SES 吞吐量

邮件正文必须包含格式正确的原始电子邮件,后者具有适当的标头字段和邮件正文编码。尽管能够在应用程序内手动构建原始邮件,但使用现有邮件库执行此操作轻松得多。

Java

以下代码示例说明如何使用JavaMail库和AWS SDK for Java来撰写和发送原始电子邮件。

package com.amazonaws.samples; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.PrintStream; import java.nio.ByteBuffer; import java.util.Properties; // JavaMail libraries. Download the JavaMail API // from https://javaee.github.io/javamail/ import javax.activation.DataHandler; import javax.activation.DataSource; import javax.activation.FileDataSource; import javax.mail.Message; import javax.mail.MessagingException; import javax.mail.Session; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; // AWS SDK libraries. Download the AWS SDK for Java // from https://aws.amazon.com/sdk-for-java import com.amazonaws.regions.Regions; import com.amazonaws.services.simpleemail.AmazonSimpleEmailService; import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClientBuilder; import com.amazonaws.services.simpleemail.model.RawMessage; import com.amazonaws.services.simpleemail.model.SendRawEmailRequest; public class AmazonSESSample { // Replace sender@example.com with your "From" address. // This address must be verified with Amazon SES. private static String SENDER = "Sender Name <sender@example.com>"; // Replace recipient@example.com with a "To" address. If your account // is still in the sandbox, this address must be verified. private static String RECIPIENT = "recipient@example.com"; // Specify a configuration set. If you do not want to use a configuration // set, comment the following variable, and the // ConfigurationSetName=CONFIGURATION_SET argument below. private static String CONFIGURATION_SET = "ConfigSet"; // The subject line for the email. private static String SUBJECT = "Customer service contact info"; // The full path to the file that will be attached to the email. // If you're using Windows, escape backslashes as shown in this variable. private static String ATTACHMENT = "C:\\Users\\sender\\customers-to-contact.xlsx"; // The email body for recipients with non-HTML email clients. private static String BODY_TEXT = "Hello,\r\n" + "Please see the attached file for a list " + "of customers to contact."; // The HTML body of the email. private static String BODY_HTML = "<html>" + "<head></head>" + "<body>" + "<h1>Hello!</h1>" + "<p>Please see the attached file for a " + "list of customers to contact.</p>" + "</body>" + "</html>"; public static void main(String[] args) throws AddressException, MessagingException, IOException { Session session = Session.getDefaultInstance(new Properties()); // Create a new MimeMessage object. MimeMessage message = new MimeMessage(session); // Add subject, from and to lines. message.setSubject(SUBJECT, "UTF-8"); message.setFrom(new InternetAddress(SENDER)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(RECIPIENT)); // Create a multipart/alternative child container. MimeMultipart msg_body = new MimeMultipart("alternative"); // Create a wrapper for the HTML and text parts. MimeBodyPart wrap = new MimeBodyPart(); // Define the text part. MimeBodyPart textPart = new MimeBodyPart(); textPart.setContent(BODY_TEXT, "text/plain; charset=UTF-8"); // Define the HTML part. MimeBodyPart htmlPart = new MimeBodyPart(); htmlPart.setContent(BODY_HTML,"text/html; charset=UTF-8"); // Add the text and HTML parts to the child container. msg_body.addBodyPart(textPart); msg_body.addBodyPart(htmlPart); // Add the child container to the wrapper object. wrap.setContent(msg_body); // Create a multipart/mixed parent container. MimeMultipart msg = new MimeMultipart("mixed"); // Add the parent container to the message. message.setContent(msg); // Add the multipart/alternative part to the message. msg.addBodyPart(wrap); // Define the attachment MimeBodyPart att = new MimeBodyPart(); DataSource fds = new FileDataSource(ATTACHMENT); att.setDataHandler(new DataHandler(fds)); att.setFileName(fds.getName()); // Add the attachment to the message. msg.addBodyPart(att); // Try to send the email. try { System.out.println("Attempting to send an email through Amazon SES " +"using the AWS SDK for Java..."); // Instantiate an Amazon SES client, which will make the service // call with the supplied AWS credentials. AmazonSimpleEmailService client = AmazonSimpleEmailServiceClientBuilder.standard() // Replace US_WEST_2 with the AWS Region you're using for // Amazon SES. .withRegion(Regions.US_WEST_2).build(); // Print the raw email content on the console PrintStream out = System.out; message.writeTo(out); // Send the email. ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); message.writeTo(outputStream); RawMessage rawMessage = new RawMessage(ByteBuffer.wrap(outputStream.toByteArray())); SendRawEmailRequest rawEmailRequest = new SendRawEmailRequest(rawMessage) .withConfigurationSetName(CONFIGURATION_SET); client.sendRawEmail(rawEmailRequest); System.out.println("Email sent!"); // Display an error if something goes wrong. } catch (Exception ex) { System.out.println("Email Failed"); System.err.println("Error message: " + ex.getMessage()); ex.printStackTrace(); } } }
Python

下面的代码示例说明如何使用 Python email.mime 程序包和适用于 Python (Boto) 的 AWS SDK 编写和发送原始电子邮件。

import json import boto3 from botocore.exceptions import ClientError from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from email.mime.application import MIMEApplication import os def boto3_rawemailv2(): SENDER = "Sender <sender@example.com>" RECIPIENT = "recipient@example.com" CONFIGURATION_SET = "ConfigSet" AWS_REGION = "us-east-1" SUBJECT = "Customer service contact info" ATTACHMENT = "path/to/customers-to-contact.xlsx" BODY_TEXT = "Hello,\r\nPlease see the attached file for a list of customers to contact." # The HTML body of the email. BODY_HTML = """\ <html> <head/> <body> <h1>Hello!</h1> <p>Please see the attached file for a list of customers to contact.</p> </body> </html> """ # The character encoding for the email. CHARSET = "utf-8" msg = MIMEMultipart('mixed') # Add subject, from and to lines. msg['Subject'] = SUBJECT msg['From'] = SENDER msg['To'] = RECIPIENT # Create a multipart/alternative child container. msg_body = MIMEMultipart('alternative') # Encode the text and HTML content and set the character encoding. This step is # necessary if you're sending a message with characters outside the ASCII range. textpart = MIMEText(BODY_TEXT.encode(CHARSET), 'plain', CHARSET) htmlpart = MIMEText(BODY_HTML.encode(CHARSET), 'html', CHARSET) # Add the text and HTML parts to the child container. msg_body.attach(textpart) msg_body.attach(htmlpart) # Define the attachment part and encode it using MIMEApplication. att = MIMEApplication(open(ATTACHMENT, 'rb').read()) # Add a header to tell the email client to treat this part as an attachment, # and to give the attachment a name. att.add_header('Content-Disposition','attachment',filename=os.path.basename(ATTACHMENT)) # Attach the multipart/alternative child container to the multipart/mixed # parent container. msg.attach(msg_body) msg.attach(att) #changes start from here strmsg = str(msg) body = bytes (strmsg, 'utf-8') client = boto3.client('sesv2') response = client.send_email( FromEmailAddress=SENDER, Destination={ 'ToAddresses': [RECIPIENT] }, Content={ 'Raw': { 'Data': body } } ) print(response) boto3_rawemailv2 ()