编码错误:在通过 AWS SES 的 MIME 文件数据中

Posted

技术标签:

【中文标题】编码错误:在通过 AWS SES 的 MIME 文件数据中【英文标题】:Encoding error: in MIME file data via AWS SES 【发布时间】:2019-07-14 06:31:00 【问题描述】:

我正在尝试通过 aws SES 从 MIME 检索附件数据,例如文件格式和文件名。不幸的是,有时文件名编码发生了变化,比如文件名是“3_amrishmishra_Entry Level Resume - 02.pdf”,在 MIME 中它显示为 '=?UTF-8?Q?amrishmishra=5FEntry_Level_Resume_=E2=80=93_02=2Epdf?= ',有什么方法可以得到准确的文件名?

if email_message.is_multipart():
message = ''
if "apply" in receiver_email.split('@')[0].split('_')[0] and isinstance(int(receiver_email.split('@')[0].split('_')[1]), int):
    for part in email_message.walk():
        content_type = str(part.get_content_type()).lower()
        content_dispo = str(part.get('Content-Disposition')).lower()
        print(content_type, content_dispo)

        if 'text/plain' in content_type and "attachment" not in content_dispo:
            message = part.get_payload()


        if content_type in ['application/pdf', 'text/plain', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'image/jpeg', 'image/jpg', 'image/png', 'image/gif'] and "attachment" in content_dispo:
            filename = part.get_filename()
            # open('/tmp/local' + filename, 'wb').write(part.get_payload(decode=True))
            # s3r.meta.client.upload_file('/tmp/local' + filename, bucket_to_upload, filename)

            data = 
                'base64_resume': part.get_payload(),
                'filename': filename,
            
            data_list.append(data)
    try:
        api_data = 
            'email_data': email_data,
            'resumes_data': data_list
        
        print(len(data_list))
        response = requests.post(url, data=json.dumps(api_data),
                                 headers='content-type': 'application/json')
        print(response.status_code, response.content)
    except Exception as e:
        print("error %s" % e)

【问题讨论】:

你的代码在哪里? @dirkgroten 已更新 :) 什么是email_message?我是说什么课?您如何从 SES 获得它? 我已经设置了将电子邮件 MIME 文件直接保存到 S3 存储桶的规则,我正在使用 Lambda 读取 MIME 文件 您能否编辑您的问题并描述您如何打开文件并将其处理为email_messageemail_message 是什么类? 【参考方案1】:

此语法'=?UTF-8?Q?...?=' 是MIME encoded word。当标头值包含非 ASCII 字符(RFC 2047 中的血腥细节)时,它在 MIME 电子邮件中使用。您的附件文件名包含一个“破折号”字符,这就是它使用这种编码发送的原因。

处理它的最佳方法取决于您使用的 Python 版本...

Python 3

Python 3 更新的 email.parser 包可以为您正确解码 RFC 2047 标头:

# Python 3
from email import message_from_bytes, policy

raw_message_bytes = b"<< the MIME message you downloaded from SES >>"
message = message_from_bytes(raw_message_bytes, policy=policy.default)
for attachment in message.iter_attachments():
    # (EmailMessage.iter_attachments is new in Python 3)
    print(attachment.get_filename())
    # amrishmishra_Entry Level Resume – 02.pdf

您必须特别请求policy.default。如果你不这样做,解析器将使用 compat32 策略来复制 Python 2.7 的错误行为 - 包括不解码 RFC 2047。(此外,早期的 Python 3 版本仍在消除新电子邮件包中的错误,所以请确保您使用的是 Python 3.5 或更高版本。)

Python 2

如果您使用的是 Python 2,最佳 选项是尽可能升级到 Python 3.5 或更高版本。 Python 2 的电子邮件解析器有许多错误和限制,这些错误和限制已通过 Python 3 中的大规模重写得到修复。(重写添加了方便的新功能,如上图所示的iter_attachments()。)

如果您无法切换到 Python 3,您可以使用 email.header.decode_header 自己解码 RFC 2047 文件名:

# Python 2 (also works in Python 3, but you shouldn't need it there)
from email.header import decode_header

filename = '=?UTF-8?Q?amrishmishra=5FEntry_Level_Resume_=E2=80=93_02=2Epdf?='
decode_header(filename)
# [('amrishmishra_Entry Level Resume \xe2\x80\x93 02.pdf', 'utf-8')]

(decoded_string, charset) = decode_header(filename)[0]
decoded_string.decode(charset)
# u'amrishmishra_Entry Level Resume – 02.pdf'

但是,如果您尝试在 Python 2.7 中解析现实世界的电子邮件,请注意,这可能只是您将遇到的几个问题中的第一个。

我维护的django-anymail 包包括email.parser.BytesParser 的compatibility version,它试图在Python 2.7 电子邮件解析中解决多个(但不是全部)other bugs。您可能可以出于您的目的借用该(内部)代码。 (或者,由于您标记了您的问题 Django,您可能想要查看 Anymail 的标准化 inbound email 处理,其中包括 Amazon SES 支持。)

【讨论】:

抱歉延迟回复。太感谢了。我在 python 2.7 上。 这有助于@AmrishMishra。我更新了我的答案,以更好地涵盖 Python 3 和 Python 2.7 的选项。 我的荣幸。 (去年花了很多时间为 Anymail 调试这些东西;很高兴这些信息可以帮助其他人。)

以上是关于编码错误:在通过 AWS SES 的 MIME 文件数据中的主要内容,如果未能解决你的问题,请参考以下文章

如何将原始电子邮件 (MIME) 从 AWS SES 转换为 Gmail?

在 iOS 上为亚马逊 SES 创建多部分/混合 MIME

通过 SMTP 使用 AWS SES 发送电子邮件,错误 421

SES AWS 错误代码:SignatureDoesNotMatch,状态代码:403

boto3 ses InvalidParameterValue 错误由于 unicode 字符

亚马逊 SES 反弹