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

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

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

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

关于电子邮件标头字段

Simple Mail Transfer Protocol (SMTP) 通过定义邮件信封及其某些参数来指定如何发送电子邮件,但它与邮件的内容无关。相反,互联网消息格式 (RFC5322) 定义了消息的构造方式。

根据 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部分末尾,在边界字符串的开头和结尾处放置两个连字符。

注意

一条消息不能超过 500 个MIME部分。

MIME编码

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

电子邮件地址

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

注意

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

电子邮件标头

要对邮件头进行编码,请使用MIME编码字语法。MIME编码后的单词语法使用以下格式:

=?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 编码的更多信息,请参阅 RFC20 45。

消息正文

要对邮件正文进行编码,可以使用 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 发送Content-Transfer-Encoding的消息中使用 8 位。但是,如果亚马逊必须SES对您的消息进行任何更改(例如,当您使用打开和点击追踪时),8位编码的内容在到达收件人的收件箱时可能无法正确显示。因此,您应始终对非 7 ASCII 位内容进行编码。

文件附件

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

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

    • 纯文本文件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 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 ()