使用 Amazon SES API v2 傳送原始電子郵件 - Amazon Simple Email Service

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 Amazon SES API v2 傳送原始電子郵件

您可以使用 Amazon SES API v2 SendEmail操作搭配指定的內容類型raw,使用原始電子郵件格式將自訂訊息傳送給收件人。

關於電子郵件標頭欄位

簡易郵件傳輸協定 (SMTP) 透過定義郵件信封與其中部分參數來指定傳送電子郵件訊息的方式,但是不會與訊息內容產生關連。反之,網際網路訊息格式 (RFC 5322) 則會定義建構訊息的方法。

在網際網路訊息格式的明確定義下,每個電子郵件訊息都會含有標題和內文。標題包含訊息中繼資料,而內文則包含訊息本身。如需有關電子郵件標題和內文的詳細資訊,請參閱 Amazon SES 中的電子郵件格式

使用原始電子郵件 MIME 訊息建構

SMTP 協定原先旨在傳送只包含 7 位元 ASCII 字元的電子郵件訊息。此規範使得 SMTP 不足以因應非 ASCII 文字編碼 (如 Unicode)、二進位內容或附件。開發多用途網際網路郵件延伸標準 (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 編碼

為維持與舊版系統的相容性,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 編碼的字詞語法。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 編碼的詳細資訊,請參閱 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: 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 程式庫和 適用於 Java 的 AWS SDK 來撰寫與傳送電子郵件原始碼。

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 適用於 Java 的 AWS SDK // 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 ()