Python Lambda将上传到s3的文件作为电子邮件附件发送

时间:2019-05-27 05:57:56

标签: python email amazon-s3 aws-lambda boto3

我们有一个在线表格,使人们可以选择上传多个文件。该表格是由第三方创建的,因此我没有任何参与。当某人使用表单上传文件时,它将文件转储到s3存储桶中的新文件夹中。我希望能够执行以下操作:

获取由表单填充程序的上传触发的文件 将文件附加到电子邮件 将电子邮件发送给特定的人。

我已经做了大量的研究,但是我仍然对编码还不陌生,并且正在尝试使用Python,我决定将其作为我的第一门适当语言。

到目前为止,我得到的代码是从我看到并调整的其他示例中借用的。但是到目前为止,仅当我将单个文件上传到存储桶的根目录时,它才向我发送包含文件的电子邮件。不会将多个文件放入存储桶中的文件夹中。这似乎是因为文件夹中文件路径中的/破坏了sendrawemail功能。

此lambda设置为由s3存储桶上的新文件创建时的PUT通知触发。 更新:我现在使用了不同的代码并找到了解决方案。请为此查看底部。

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

def lambda_handler(event, context):
    file_obj = event["Records"][0]
    bucket_name = str(file_obj['s3']['bucket']['name'])
    key = str(file_obj['s3']['object']['key'])
    msg = MIMEMultipart()
    new_body = "test body"
    text_part = MIMEText(new_body, _subtype="html")
    msg.attach(text_part)

    filename = str(event["Records"][0]['s3']['object']['key'])
    msg["To"] = "test@test.com"
    msg["From"] = "test@test.com"
    s3_object = boto3.client('s3')
    s3_object = s3_object.get_object(Bucket=str(bucket_name), Key=str(key))
    body = s3_object['Body'].read()

    part = MIMEApplication(body, filename)
    part.add_header("Content-Disposition", 'attachment', filename=filename)
    msg.attach(part)
    ses_aws_client = boto3.client('ses', 'eu-west-1')
    ses_aws_client.send_raw_email(RawMessage={"Data" : msg.as_bytes()})

我希望的是,当一个文件夹或多个文件上传时,我会收到一封包含所有文件作为附件的电子邮件(我知道消息的限制为10mb)。但是,当有多个文件时,似乎正在发送多封电子邮件,每封电子邮件一个文件。而且,如果文件在文件夹中,因此具有一个带有斜杠的键值,则send_raw_email将显示以下错误:

[ERROR] ClientError: An error occurred (InvalidParameterValue) when calling the SendRawEmail operation: Expected ';', got "/"

我认为我需要以某种方式对路径进行编码?有什么方法可以整理所有新上传到一封电子邮件中的文件?

非常欢迎任何帮助。

编辑1:根据Jeril的回复,我正在共享失败中的完整日志条目。另外,我在原始代码块中错过了一行,所以我也将对其进行更新。

12:14:59
[ERROR] ClientError: An error occurred (InvalidParameterValue) when calling the SendRawEmail operation: Expected ';', got "/" Traceback (most recent call last):   File "/var/task/lambda_function.py", line 26, in lambda_handler     ses_aws_client.send_raw_email(RawMessage={"Data" : msg.as_bytes()})   File "/var/runtime/botocore/client.py", line 320, in _api_call     return self._make_api_call(opera
[ERROR] ClientError: An error occurred (InvalidParameterValue) when calling the SendRawEmail operation: Expected ';', got "/"
Traceback (most recent call last):
  File "/var/task/lambda_function.py", line 26, in lambda_handler
    ses_aws_client.send_raw_email(RawMessage={"Data" : msg.as_bytes()})
  File "/var/runtime/botocore/client.py", line 320, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/var/runtime/botocore/client.py", line 623, in _make_api_call
    raise error_class(parsed_response, operation_name)

更新: 我现在设法获得了这项工作的基本功能:

import os.path
import boto3
import email
from botocore.exceptions import ClientError
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication


s3 = boto3.client("s3")

def lambda_handler(event, context):
    # Replace sender@example.com with your "From" address.
    # This address must be verified with Amazon SES.
    SENDER = "Test Test <test@test.com>"

    # Replace recipient@example.com with a "To" address. If your account 
    # is still in the sandbox, this address must be verified.
    RECIPIENT = "Test Test <test@test.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"

    AWS_REGION = "eu-west-1"
    SUBJECT = "Test Send Mesage with Attachment"

    # This is the start of the process to pull the files we need from the S3 bucket into the email.
    # Get the records for the triggered event
    FILEOBJ = event["Records"][0]
    # Extract the bucket name from the records for the triggered event
    BUCKET_NAME = str(FILEOBJ['s3']['bucket']['name'])
    # Extract the object key (basicaly the file name/path - note that in S3 there are 
    # no folders, the path is part of the name) from the records for the triggered event
    KEY = str(FILEOBJ['s3']['object']['key'])

    # extract just the last portion of the file name from the file. This is what the file
    # would have been called prior to being uploaded to the S3 bucket
    FILE_NAME = os.path.basename(KEY) 

    # Using the file name, create a new file location for the lambda. This has to
    # be in the tmp dir because that's the only place lambdas let you store up to
    # 500mb of stuff, hence the '/tmp/'+ prefix
    TMP_FILE_NAME = '/tmp/' +FILE_NAME

    # Download the file/s from the event (extracted above) to the tmp location
    s3.download_file(BUCKET_NAME, KEY, TMP_FILE_NAME)

    # Make explicit that the attachment will have the tmp file path/name. You could just
    # use the TMP_FILE_NAME in the statments below if you'd like.
    ATTACHMENT = TMP_FILE_NAME

    # The email body for recipients with non-HTML email clients.
    BODY_TEXT = "Hello,\r\nPlease see the attached file related to recent submission."

    # The HTML body of the email.
    BODY_HTML = """\
    <html>
    <head></head>
    <body>
    <h1>Hello!</h1>
    <p>Please see the attached file related to recent submission.</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'])

现在唯一的问题是,如果我上传多个文件,则每个文件都会收到一封电子邮件。有没有办法将它们全部整理成一封电子邮件?

0 个答案:

没有答案