python 3 smtplib:当gnupg处于活动状态时,二进制附件在烧瓶中编码不正确
Posted
技术标签:
【中文标题】python 3 smtplib:当gnupg处于活动状态时,二进制附件在烧瓶中编码不正确【英文标题】:python 3 smtplib: binary attachment encodes incorrectly in flask when gnupg is active 【发布时间】:2017-02-28 08:03:02 【问题描述】:我正在尝试使用 python 3.5.2 smtplib 通过 TLS 发送二进制附件。我的平台是 OSX,我使用的是从 homebrew 安装的 python。
当我收到附件时,编码似乎被修改了。而不是以这个十六进制开头的原始文件:
ffd8 ffe0 0010 4a46 4946 0001 0100 0001
i.e., <FF><D8><FF><E0>^@^PJFIF^@^A^A
我收到的附件的起始十六进制有一些奇怪的 base64 剩余:
5c75 6463 6666 5c75 6463 6438 5c75 6463 6666 5c75 6463 6530 0010 4a46 4946 0001
i.e., \udcff\udcd8\udcff\udce0^@^PJFIF
这是一个失败的最小案例,除了添加了 TLS 逻辑和 gnupg:
import gnupg
gpg = gnupg.GPG('/path/to/gpg')
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
def send_my_email():
msg = MIMEMultipart()
msg['Subject'] = 'subject'
msg['From'] = 'XXXX@gmail.com'
msg['To'] = 'YYYY@gmail.com'
with open('/tmp/image.jpg', mode='rb') as image_file:
image = MIMEImage(image_file.read())
msg.attach(image)
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
s.login('XXXX@gmail.com', 'password')
s.send_message(msg)
s.quit()
基于此处的另一个问题,我尝试了这个而不是send_message()
,但它也失败了:
s.sendmail('XXXX@gmail.com', ['YYYY@gmail.com'], msg.as_string())
我还尝试在初始化MIMEImage
时显式添加_subtype='jpg'
,添加Content-Transfer-Encoding
标头,并添加Content-Disposition
标头,但这些似乎都没有任何区别。
我已验证我的电子邮件客户端在收到来自其他客户端的 base64 编码附件时没有问题。
我查看了 smtplib 源代码并注意到 smtplib 处理行分隔符的方式看起来有点奇怪,我想知道这是否可能相关。 (另见参考:https://bugs.python.org/issue14645)
我是否需要对某些内容进行不同的编码,为我的平台设置一些特殊的内容,或者这是一个小故障?谢谢!
更新:此问题仅在我运行 Flask 时存在,不会在 Flask 之外发生。我正在尝试隔离我的 Flask 环境中的问题。我认为它可能是 flask_mail 但删除它并没有解决问题。下面的代码在我的系统上从 Flask 运行时会失败,但如果我从同一虚拟环境和同一 python 二进制文件中的 shell 脚本运行它则不会。由于复杂性,我目前不希望有任何答案,但会留给后代。
更新 2:我已将此问题缩小到与来自 https://github.com/isislovecruft/python-gnupg/ 的 gnupg 库的交互。我已经更新了我的最小示例以反映这一点。 python-gnupug 中的这个 python 编解码器的猴子补丁负责这个问题:codecs.register_error('strict', codecs.replace_errors)
【问题讨论】:
看起来文件正在被读入 python 字符串(unicode)并在 utf-8 编码该字符串后被序列化。我怀疑 MIMEImage 需要一些帮助,但我没有在 3.x 上使用 email.mime 代码,所以我无法直接指出问题所在。 啊哈,当我读入文件时,我会看看是否需要做一些不同的事情。谢谢! 在将app.open_resource()
转换为open()
后,您的程序对我来说完美无缺。您能否提供一个简短的完整程序来演示该问题?请参阅minimal reproducible example 了解更多信息。
此外,将其集成到 Flask 应用程序中仍然不会使其失败。请提供minimal reproducible example。
啊,我忽略了从我的示例中清除 open_resource 代码。我确实在使用 Flask,并且使用此代码确实会出现这个问题。 image.jpg 位于我的 Flask 应用程序的根目录中。我将尝试直接打开,看看它是否与我的 Flask 调用有关。
【参考方案1】:
这是由于 gnupg 和 smtplib + MIME 之间的不良交互造成的。 gnupg 调用codecs.register_error('strict', codecs.replace_errors)
,会干扰其他包进行的编码。
参考:https://github.com/isislovecruft/python-gnupg/issues/49
【讨论】:
以上是关于python 3 smtplib:当gnupg处于活动状态时,二进制附件在烧瓶中编码不正确的主要内容,如果未能解决你的问题,请参考以下文章
python 3 smtplib 异常:“SSL:WRONG_VERSION_NUMBER”登录到 Outlook