当前位置: 首页 > 知识库问答 >
问题:

修改后的AWS SES转发示例代码(Python)无法转发电子邮件正文

年高洁
2023-03-14

我试图让AWS SES Lambda转发Python示例代码转发电子邮件到外部帐户未修改。在下面的(A)中,除非电子邮件是多部分的,否则邮件正文不会正确转发。大多数错误与:

body=MIMEText(str(mailobject.get_payload(decode=True),'UTF-8'))

如果我正确理解python留档,当MIMEMultipart为false时,应该将电子邮件正文作为字符串传递给正文。根据该行代码是如何完成的,它将发送如下所示的主体,或者编译器将返回错误,如(B)所示或'str'对象没有属性'策略',。

当我收到一封电子邮件时,它是什么样子。html格式显示为文本。

<html>
        <body>
                <p>You have chosen to subscribe to the topic:
                <br /><b>arn:aws:sns:I INTENTIONALLY REMOVED THIS</b></p>
                <p>To confirm this subscription, click or visit the link below (If this was in error no action is necessary):
                <br /><a href="https://sns.us-east-1.amazonaws.com/confirmation.html?TopicArn=arn:aws:sns:us-east-1:I INTENTIONALLY REMOVED THIS</a></p>
                <p><small>Please do not reply directly to this email. If you wish to remove yourself from receiving all future SNS subscription confirmation requests please send an email to <a href="mailto:sns-opt-out@amazon.com">sns-opt-out</a></small></p>
        </body>
</html>

(a)我使用的代码:

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

region = os.environ['Region']

def get_time():
    
    # datetime object containing current date and time
    now = datetime.now()
    
    # dd/mm/YY H:M:S
    return now.strftime("%d/%m/%Y %H:%M:%S")

def get_message_from_s3(message_id):

    incoming_email_bucket = os.environ['MailS3Bucket']
    incoming_email_prefix = os.environ['MailS3Prefix']

    if incoming_email_prefix:
        object_path = (incoming_email_prefix + "/" + message_id)
    else:
        object_path = message_id

    object_http_path = (f"http://s3.console.aws.amazon.com/s3/object/{incoming_email_bucket}/{object_path}?region={region}")

    # Create a new S3 client.
    client_s3 = boto3.client("s3")

    # Get the email object from the S3 bucket.
    object_s3 = client_s3.get_object(Bucket=incoming_email_bucket,
        Key=object_path)
    # Read the content of the message.
    file = object_s3['Body'].read()

    file_dict = {
        "file": file,
        "path": object_http_path
    }

    return file_dict

def create_message(file_dict):

    stringMsg = file_dict['file'].decode('utf-8')

    # Create a MIME container.
    msg = MIMEMultipart('alternative')

    sender = os.environ['MailSender']
    recipient = os.environ['MailRecipient']

    # Parse the email body.
    mailobject = email.message_from_string(file_dict['file'].decode('utf-8'))
    #print(mailobject.as_string())

    # Get original sender for reply-to
    from_original = mailobject['Return-Path']
    from_original = from_original.replace('<', '');
    from_original = from_original.replace('>', '');
    print(from_original)

    # Create a new subject line.
    subject = mailobject['Subject']

    body = ""

    if mailobject.is_multipart():

        print('IS multipart')
        index = stringMsg.find('Content-Type: multipart/')
        stringBody = stringMsg[index:]
        #print(stringBody)
        stringData = 'Subject: ' + subject + '\nTo: ' + sender + '\nreply-to: ' + from_original + '\n' + stringBody

        message = {
            "Source": sender,
            "Destinations": recipient,
            "Data": stringData
        }
        return message

        for part in mailobject.walk():
            ctype = part.get_content_type()
            cdispo = str(part.get('Content-Disposition'))

            # case for each common content type
            if ctype == 'text/plain' and 'attachment' not in cdispo:
                bodyPart = MIMEText(part.get_payload(decode=True), 'plain', part.get_content_charset())
                msg.attach(bodyPart)

            if ctype == 'text/html' and 'attachment' not in cdispo:
                mt = MIMEText(part.get_payload(decode=True), 'html', part.get_content_charset())
                email.encoders.encode_quopri(mt)
                del mt['Content-Transfer-Encoding']
                mt.add_header('Content-Transfer-Encoding', 'quoted-printable')
                msg.attach(mt)

            if 'attachment' in cdispo and 'image' in ctype:
                mi = MIMEImage(part.get_payload(decode=True), ctype.replace('image/', ''))
                del mi['Content-Type']
                del mi['Content-Disposition']
                mi.add_header('Content-Type', ctype)
                mi.add_header('Content-Disposition', cdispo)
                msg.attach(mi)

            if 'attachment' in cdispo and 'application' in ctype:
                ma = MIMEApplication(part.get_payload(decode=True), ctype.replace('application/', ''))
                del ma['Content-Type']
                del ma['Content-Disposition']
                ma.add_header('Content-Type', ctype)
                ma.add_header('Content-Disposition', cdispo)
                msg.attach(ma)


    # not multipart - i.e. plain text, no attachments, keeping fingers crossed
    else:
        
        body = MIMEText(str(mailobject.get_payload(decode=True), 'UTF-8'))
        msg.attach(body)

    # The file name to use for the attached message. Uses regex to remove all
    # non-alphanumeric characters, and appends a file extension.
    filename = re.sub('[^0-9a-zA-Z]+', '_', subject)

    # Add subject, from and to lines.
    msg['Subject'] = subject
    msg['From'] = sender
    msg['To'] = recipient
    msg['reply-to'] = mailobject['Return-Path']
    
    # Create a new MIME object.
    att = MIMEApplication(file_dict["file"], filename)
    att.add_header("Content-Disposition", 'attachment', filename=filename)

    # Attach the file object to the message.
    msg.attach(att)
    message = {
        "Source": sender,
        "Destinations": recipient,
        "Data": msg.as_string()
    }
    return message
    
def send_email(message):
    aws_region = os.environ['Region']

# Create a new SES client.
    client_ses = boto3.client('ses', region)

    # Send the email.
    try:
        #Provide the contents of the email.
        response = client_ses.send_raw_email(
            Source=message['Source'],
            Destinations=[
                message['Destinations']
            ],
            RawMessage={
                'Data':message['Data']
            }
        )

    # Display an error if something goes wrong.
    except ClientError as e:
        output = e.response['Error']['Message']
    else:
        output = "Email sent! Message ID: " + response['MessageId']

    return output

def lambda_handler(event, context):
    # Get the unique ID of the message. This corresponds to the name of the file
    # in S3.
    message_id = event['Records'][0]['ses']['mail']['messageId']
    print(f"Received message ID {message_id}")

    # Retrieve the file from the S3 bucket.
    file_dict = get_message_from_s3(message_id)

    # Create the message.
    message = create_message(file_dict)
    #this alone didn't fix it:   message = create_message(str(file_dict))
    # Send the email and print the result.
    result = send_email(message)
    print(result)

(B) 。这篇文章中的代码示例在“body”任务中失败了,我一直在尝试(A)。。。

body = MIMEText(mailobject.get_payload(decode=True), 'UTF-8')
    msg.attach(body)
Response:
{
  "errorMessage": "'bytes' object has no attribute 'encode'",
  "errorType": "AttributeError",
  "stackTrace": [
    "  File \"/var/task/lambda_function.py\", line 190, in lambda_handler\n    message = create_message(file_dict)\n",
    "  File \"/var/task/lambda_function.py\", line 128, in create_message\n    body = MIMEText(mailobject.get_payload(decode=True), 'UTF-8')\n",
    "  File \"/var/lang/lib/python3.8/email/mime/text.py\", line 34, in __init__\n    _text.encode('us-ascii')\n"
  ]
}

要复制此问题,请启动一个新的SES实例,遵循原始AWS SES Lambda转发Python示例代码的教程,并验证其工作原理。从那里,用(A)中的代码或其他链接示例中的一个替换AWS示例代码。

此外,我还发现了这个Lambda电子邮件测试事件代码,并能够使其充分工作,以便从AWS Lambda仪表板测试该代码。为了让它正常工作,我在仪表板中创建了两个新的测试事件(多部分/非多部分),并粘贴在代码中,更改(a)电子邮件地址(如适用),(b)lambda函数ARN到我函数的ARN,以及(c)存储在活动s3实例上的适用电子邮件的消息id。这帮我省去了手动创建和发送测试邮件以进行调试的麻烦,希望其他人也能找到这篇文章并遇到类似问题。该测试代码将导致转发实际的电子邮件。

共有1个答案

云捷
2023-03-14

对于出现此错误的用户:

'bytes' object has no attribute 'encode'

在这一行中:

body = MIMEText(mailobject.get_payload(decode=True), 'UTF-8')

我能做到。我不是这方面的专家,所以代码可能需要一些改进。此外,电子邮件正文还包含html标记。但至少它被送到了。

如果解码电子邮件仍然失败,错误消息将出现在您的CloudWatch日志中。此外,您将收到一封带有错误信息的电子邮件。

payload = mailobject.get_payload(decode=True)
try:
    decodedPayload = payload.decode()
    body = MIMEText(decodedPayload, 'UTF-8')
    msg.attach(body)
except Exception as error:
    errorMsg = "An error occured when decoding the email payload:\n" + str(error)
    print(errorMsg)
    body = errorMsg + "\nPlease download it manually from the S3 bucket."
    msg.attach(MIMEText(body, 'plain'))

您可以自行决定要将哪些信息添加到错误电子邮件中,如主题或来自地址。

 类似资料:
  • 问题内容: 我正在尝试编写一个脚本,该脚本会自动将符合特定条件的某些电子邮件转发到另一封电子邮件。 我已经可以使用imaplib和电子邮件来下载和解析消息了,但是我不知道如何将整个电子邮件转发到另一个地址。我是否需要从头开始构建新消息,还是可以以某种方式修改旧消息并重新发送? 到目前为止,这是我所拥有的(客户端是imaplib.IMAP4连接,而id是消息ID): 我敢肯定,关于邮件的MIME内容

  • 在本章中,我们将了解如何使用JavaMail API转发电子邮件。 以下程序中遵循的基本步骤是: 在属性中获取具有POP和SMPT服务器详细信息的Session对象。 我们需要POP详细信息来检索消息和SMPT详细信息以发送消息。 创建POP3商店对象并连接到商店。 创建文件夹对象并在邮箱中打开相应的文件夹。 检索邮件。 如果要转发,请浏览消息并输入“Y”或“y”。 获取邮件的所有信息(收件人,发

  • null null 类型:host:值: 类型:host:值: 类型:host:值: 试图访问会带来一个页面。 如何正确地配置Heroku和Namecheap以使其工作?

  • 您可以根据需要在cPanel电子邮件中创建多个电子邮件帐户。 当电子邮件帐户数量增加时,您需要打开每个帐户才能查看收到的电子邮件。 要解决此问题,您可以将一个帐户的这些电子邮件转发到另一个帐户,因此现在您只需打开一个帐户即可查看所有电子邮件。 您也可以丢弃该电子邮件或将其发送到程序。 您还可以将域的所有电子邮件转发到其他域。 例如,您有几个电子邮件ID,如info@wenjiangs.com ad

  • 我的: 但是邮件不会发送到该地址,在控制台中,当我单击时,正在打印,但它不会发送任何电子邮件。 我用的是Django 1.3.7和Python 2.6。 不知道是版本的问题还是什么逻辑问题。

  • 本文向大家介绍ThinkPHP发送邮件示例代码,包括了ThinkPHP发送邮件示例代码的使用技巧和注意事项,需要的朋友参考一下 先在GitHub找到PHPMailer 并下载 https://github.com/PHPMailer/PHPMailer  //PHPMailer的GitHub地址 将下载的PHPMailer放在ThinkPHP/Library/Vendor目录下      在con