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

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

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

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

关于电子邮件标头字段

简单邮件传输协议 (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 encoded-word 语法编码“易记发件人”名称,如使用 Amazon SES API v2 发送原始电子邮件中所述。有关 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 编码。如果编码值为 B,则 encoded-text 的值必须使用 base64 编码。

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

  • Q 编码

    =?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 编码的更多信息,请参阅 RFC 2047。有关 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 不接受的文件类型的列表,请参阅Amazon 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 程序包和AWS SDK for Python (Boto) 编写和发送原始电子邮件。

import os 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 # Replace sender@example.com with your "From" address. # This address must be verified with Amazon SES. 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. 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. CONFIGURATION_SET = "ConfigSet" # If necessary, replace us-west-2 with the AWS Region you're using for Amazon SES. AWS_REGION = "us-west-2" # The subject line for the email. SUBJECT = "Customer service contact info" # The full path to the file that will be attached to the email. ATTACHMENT = "path/to/customers-to-contact.xlsx" # The email body for recipients with non-HTML email clients. 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></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" # Create a new SES resource and specify a region. client = boto3.client('ses',region_name=AWS_REGION) # Create a multipart/mixed parent container. 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) # Add the attachment to the parent container. msg.attach(att) #print(msg) try: #Provide the contents of the email. response = client.send_raw_email( Source=SENDER, Destinations=[ RECIPIENT ], RawMessage={ 'Data':msg.as_string(), }, ConfigurationSetName=CONFIGURATION_SET ) # Display an error if something goes wrong. except ClientError as e: print(e.response['Error']['Message']) else: print("Email sent! Message ID:"), print(response['MessageId'])