【问题标题】:AWS EC2 Email Forwarding: Forward Incoming Email to an External Destination with LambdaAWS EC2 电子邮件转发:使用 Lambda 将传入电子邮件转发到外部目的地
【发布时间】:2021-09-29 23:31:43
【问题描述】:

我正在按照 AWS 教程将电子邮件转发到我的 Gmail 帐户,该教程位于此处:https://aws.amazon.com/blogs/messaging-and-targeting/forward-incoming-email-to-an-external-destination/

我已经完成了所有这些工作,但是转发的电子邮件以附件的形式出现,必须下载才能阅读。

我想知道的是如何让这个转发器使电子邮件看起来像普通电子邮件一样。我搜索了 SO,但发现关于该主题的线程很少。因此,我自己研究并弄清楚了,并在下面为其他关注此问题的人提供了答案。

【问题讨论】:

    标签: python amazon-web-services amazon-ec2 amazon-ses


    【解决方案1】:

    使用以下 Python 代码代替教程中提供的代码。您转发的电子邮件将以您想要的方式到达。

    请记住,您的 lambda 函数可以运行多长时间是有时间限制的。解码和编码大型附件需要时间。因此,任何太大的附件(如音频/视频文件)都可能导致整个转发失败。但是,该失败不会阻止 AWS 一次又一次地重试,并且每次尝试都会再次超时。

    # Copyright 2010-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    # Altered from original by Adam Winter
    #
    # This file is licensed under the Apache License, Version 2.0 (the "License").
    # You may not use this file except in compliance with the License. A copy of the
    # License is located at
    #
    # http://aws.amazon.com/apache2.0/
    #
    # This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
    # OF ANY KIND, either express or implied. See the License for the specific
    # language governing permissions and limitations under the License.
    
    import os
    import boto3
    import email
    import re
    import html
    from botocore.exceptions import ClientError
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    from email.mime.application import MIMEApplication
    from email.mime.image import MIMEImage
    
    region = os.environ['Region']
    
    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']
        print(subject)
    
        if mailobject.is_multipart():
    
            #The quick and dirty way.  If you don't like this, use the for loop below it.
            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(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_original)
    
        # 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:
            print('send email ClientError Exception')
            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)
    
        # Send the email and print the result.
        result = send_email(message)
        print(result)
    

    【讨论】:

      【解决方案2】:

      你可以无视下面这条消息,我看到你的cmets关于“又快又脏”,让我探索

      return message 是错字吗,在这段代码中没有意义吗?

          if mailobject.is_multipart():
      
              #The quick and dirty way.  If you don't like this, use the for loop below it.
              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
      

      顺便说一句:感谢您提供此代码 - 我一直在寻找附件示例的替代方法,但不想纠结于您在做什么,所以谢谢!

      【讨论】:

      • 不是错字。这将返回快速而肮脏的结果(这似乎工作得很好),只需那几行代码即可完成。自然,它会跳过该方法中的其余代码。如果您想以费力的方式进行操作,可以将其注释掉并使用该方法中的其余代码。我开始用费力的方式做这件事,并让它适用于那几种电子邮件附件,然后想出了快速而肮脏的方法。因此,出于教育目的,我将两个版本都留在那里。
      • 感谢您的解释。让我探索一下,再次感谢!
      猜你喜欢
      • 2014-07-06
      • 2013-07-29
      • 1970-01-01
      • 2019-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-25
      相关资源
      最近更新 更多