如何使用boto3 send_email或send_raw_email发送HTML文本和附件?

时间:2017-03-24 11:18:31

标签: python python-2.7 boto3

如何使用boto3 SES Quantidade客户端发送图片附件?

我知道我可以使用send_email发送附件,但我需要使用send_raw_email发送邮件正文。如果无法做到这一点,我如何使用boto3.ses.send_raw_email()发送包含html数据的电子邮件?

7 个答案:

答案 0 :(得分:9)

浏览了包括其他SO问题,博客和Python文档在内的多个资源之后,我想到了下面的代码。

允许使用文本和/或html电子邮件和附件。

将MIME和boto3部分分开,以防万一您想将MIME再次用于其他目的,例如使用SMTP客户端而不是boto3发送电子邮件。

import os
import boto3
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication


def create_multipart_message(
        sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None)\
        -> MIMEMultipart:
    """
    Creates a MIME multipart message object.
    Uses only the Python `email` standard library.
    Emails, both sender and recipients, can be just the email string or have the format 'The Name <the_email@host.com>'.

    :param sender: The sender.
    :param recipients: List of recipients. Needs to be a list, even if only one recipient.
    :param title: The title of the email.
    :param text: The text version of the email body (optional).
    :param html: The html version of the email body (optional).
    :param attachments: List of files to attach in the email.
    :return: A `MIMEMultipart` to be used to send the email.
    """
    multipart_content_subtype = 'alternative' if text and html else 'mixed'
    msg = MIMEMultipart(multipart_content_subtype)
    msg['Subject'] = title
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)

    # Record the MIME types of both parts - text/plain and text/html.
    # According to RFC 2046, the last part of a multipart message, in this case the HTML message, is best and preferred.
    if text:
        part = MIMEText(text, 'plain')
        msg.attach(part)
    if html:
        part = MIMEText(html, 'html')
        msg.attach(part)

    # Add attachments
    for attachment in attachments or []:
        with open(attachment, 'rb') as f:
            part = MIMEApplication(f.read())
            part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment))
            msg.attach(part)

    return msg


def send_mail(
        sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None) -> dict:
    """
    Send email to recipients. Sends one mail to all recipients.
    The sender needs to be a verified email in SES.
    """
    msg = create_multipart_message(sender, recipients, title, text, html, attachments)
    ses_client = boto3.client('ses')  # Use your settings here
    return ses_client.send_raw_email(
        Source=sender,
        Destinations=recipients,
        RawMessage={'Data': msg.as_string()}
    )


if __name__ == '__main__':
    sender_ = 'The Sender <the_sender@email.com>'
    recipients_ = ['Recipient One <recipient_1@email.com>', 'recipient_2@email.com']
    title_ = 'Email title here'
    text_ = 'The text version\nwith multiple lines.'
    body_ = """<html><head></head><body><h1>A header 1</h1><br>Some text."""
    attachments_ = ['/path/to/file1/filename1.txt', '/path/to/file2/filename2.txt']

    response_ = send_mail(sender_, recipients_, title_, text_, body_, attachments_)
    print(response_)

答案 1 :(得分:4)

来自&#34; HOW TO SEND HTML MAILS USING AMAZON SES &#34;的无耻复制示例 这就是典型的电子邮件数据内容的样子。

 message_dict = { 'Data':
  'From: ' + mail_sender + '\n'
  'To: ' + mail_receivers_list + '\n'
  'Subject: ' + mail_subject + '\n'
  'MIME-Version: 1.0\n'
  'Content-Type: text/html;\n\n' +
  mail_content}

如果您想使用boto3.ses.send_raw_email发送附件和HTML文本,则只需使用上面的消息dict并传递即可。 (只需将你的html文本放在mail_content下)

response = client.send_raw_email(
    Destinations=[
    ],
    FromArn='',
    RawMessage=message_dict,
    ReturnPathArn='',
    Source='',
    SourceArn='',
)

实际上,原始附件标头应该在send_email()和send_raw_email()中都有效。除send_mail外,您应将附件放在Text内,而不是html

答案 2 :(得分:3)

这对我发送附件很有用:

from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

import boto.ses


AWS_ACCESS_KEY = 'HEREYOURACCESSKEY'
AWS_SECRET_KEY = 'HEREYOURSECRETKEY'

class Email(object):

    def __init__(self, to, subject):
        self.to = to
        self.subject = subject
        self.text = None
        self.attachment = None


    def text(self, text):
        self.text = text

    def add_attachment(self, attachment):
        self.attachment = attachment

    def send(self, from_addr=None, file_name = None):

        connection = boto.ses.connect_to_region(
            'us-east-1',
            aws_access_key_id=AWS_ACCESS_KEY,
            aws_secret_access_key=AWS_SECRET_KEY
        )
        msg = MIMEMultipart()
        msg['Subject'] = self.subject
        msg['From'] = from_addr
        msg['To'] = self.to

        part = MIMEApplication(self.attachment)
        part.add_header('Content-Disposition', 'attachment', filename=file_name)
        part.add_header('Content-Type', 'application/vnd.ms-excel; charset=UTF-8')

        msg.attach(part)

        # the message body
        part = MIMEText(self.text)
        msg.attach(part)

        return connection.send_raw_email(msg.as_string(),source=from_addr,destinations=self.to)

if __name__ == "__main__":
    email = Email(to='toMail@gmail.com', subject='Your subject!')
    email.text('This is a text body.')
    #you could use StringIO.StringIO() to get the file value
    email.add_attachment(yourFileValue)
    email.send(from_addr='from@mail.com',file_name="yourFile.txt")

答案 3 :(得分:2)

要扩展@adkl的答案,亚马逊自己的示例是使用旧的Python处理电子邮件和附件的方式。没关系,只是这些模块上的当前文档不全面,可能会使像我这样的新用户感到困惑。

这是有关通过CSV附件形成邮件的简单示例。

from email.message import EmailMessage


def create_email_message(sender: str, recipients: list, title: str, text: str,
                         attachment: BytesIO, file_name: str) -> EmailMessage:
    msg = EmailMessage()
    msg["Subject"] = title
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)
    msg.set_content(text)
    data = attachment.read()
    msg.add_attachment(
        data,
        maintype="text",
        subtype="csv",
        filename=file_name
    )
    return msg

# Client init, attachment file creation here

message = create_email_message(...)
try:
    ses.send_raw_email(
        Source=sender,
        Destinations=recipients,
        RawMessage={'Data': message.as_string()}
    )
except ClientError as e:
    logger.exception(f"Cannot send email report to {recipients}: {e}")
else:
    logger.info("Sent report successfully")

在此示例中,我使用BytesIO对象作为附件的来源,但是您可以使用任何支持read()方法的类似文件的对象。

答案 4 :(得分:2)

这是我最后使用的课程。放入Lambda文件并使用它。

接受附件的文件名列表。发送HTML电子邮件。为\n更改<br />

我将下面的类[1]保存为emailer.py并用作:

from emailer import Emailer

def lambda_handler(event, context):
  # ...
  emailer = Emailer()
  emailer.send(
    to=email_recipients_list, 
    subject=subject_string, 
    fromx=from_address_string, 
    body=email_body_string, 
    attachments=attachments_list
  )

[1]

import boto3
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart


class Emailer(object):
    """ send email with attachments """
    
    def send(self, to, subject, fromx, body=None, content=None, attachments=None):
        """ sends email with attachments
        
        Parameters:
            * to (list or comma separated string of addresses): recipient(s) address
            * fromx (string): from address of email
            * body (string, optional): Body of email ('\n' are converted to '< br/>')
            * content (string, optional): Body of email specified as filename
            * attachments (list, optional): list of paths of files to attach
        """

        if attachments is None:
            attachments = []

        self.to = to
        self.subject = subject
        self.fromx = fromx
        self.attachment = None
        self.body = body
        self.content = content
        self.attachments = attachments
        
        if type(self.to) is list:
            self.to = ",".join(self.to)
        
        
        message = MIMEMultipart()
        
        message['Subject'] = self.subject
        message['From'] = self.fromx
        message['To'] = self.to
        if self.content and os.path.isfile(self.content):
            part = MIMEText(open(str(self.content)).read().replace("\n", "<br />"), "html")
            message.attach(part)
        elif self.body:
            part = MIMEText(self.body.replace("\\n", "<br />").replace("\n", "<br />"), "html")
            message.attach(part)
            
        
        for attachment in self.attachments:
            part = MIMEApplication(open(attachment, 'rb').read())
            part.add_header('Content-Disposition', 'attachment', filename=attachment.split("/")[-1])
            message.attach(part)
            
        ses = boto3.client('ses', region_name='us-east-1')
        
        response = ses.send_raw_email(
            Source=message['From'],
            Destinations=message['To'].split(","),
            RawMessage={
                'Data': message.as_string()
            }
        )

答案 5 :(得分:1)

2019年3月

以下是已更新官方文档(https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-raw.html)中粘贴复制的解决方案:

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'])

答案 6 :(得分:0)

这也可以使用python版本2.7.x来实现

以下是此的工作代码-

[注意-您添加的“发件人”和“收件人”必须在AWS SES中进行验证。或SES必须从“沙盒”状态移至“生产”状态

import os
import boto3
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication


def create_multipart_message(email_metadata):        
    sender = email_metadata['sender_']
    recipients = email_metadata['recipients_']
    title = email_metadata['title_']
    text = email_metadata['text_']
    html = email_metadata['body_']
    attachments = email_metadata['attachments_']

    multipart_content_subtype = 'alternative' if text and html else 'mixed'
    msg = MIMEMultipart(multipart_content_subtype)
    msg['Subject'] = title
    msg['From'] = sender
    msg['To'] = ', '.join(recipients)

    # Record the MIME types of both parts - text/plain and text/html.
    # According to RFC 2046, the last part of a multipart message, in this case the HTML message, is best and preferred.
    if text:
        part = MIMEText(text, 'plain')
        msg.attach(part)
    if html:
        part = MIMEText(html, 'html')
        msg.attach(part)

    # Add attachments
    for attachment in attachments or []:
        with open(attachment, 'rb') as f:
            part = MIMEApplication(f.read())
            part.add_header('Content-Disposition', 'attachment', filename=os.path.basename(attachment))
            msg.attach(part)

    return msg


def send_mail(email_metadata):
        #sender: str, recipients: list, title: str, text: str=None, html: str=None, attachments: list=None) -> dict:
    """
    Send email to recipients. Sends one mail to all recipients.
    The sender needs to be a verified email in SES.
    """
    msg = create_multipart_message(email_metadata)
    ses_client = boto3.client('ses')  # Use your settings here
    return ses_client.send_raw_email(
        Source=email_metadata['sender_'],
        Destinations=email_metadata['recipients_'],
        RawMessage={'Data': msg.as_string()}
    )


if __name__ == '__main__':
    email_metadata = {
    "sender_" : "The Sender <the_sender@email.com>",
    "recipients_" : ['Recipient One <recipient_1@email.com>','Recipient two <recipient_2@email.com>'],
    "title_" : "Email title here",
    "text_" : "The text version\nwith multiple lines.",
    "body_" : "<html><head></head><body><h1>A header 1</h1><br>Some text.",
    "attachments_" : ['/path/to/file1/filename1.txt','/path/to/file2/filename2.txt']
    }

    response_ = send_mail(email_metadata)
    print(response_)