Python smtplib.sendmail 的多部分 Mime 邮件在 iPhone 上不显示

9 投票
2 回答
5168 浏览
提问于 2025-04-18 04:56

这是我的代码:

FROM = ''
TO = ''
SMTPSERVER = ''
MYEMAILPASSWORD = ""

import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.mime.base import MIMEBase
from email import encoders


def physicallySend(listOfAttachments):
    msg = MIMEMultipart('alternative')
    msg['Subject'] = "Testing"
    msg['From'] = FROM
    msg['To'] = TO

    textPart = MIMEText("Hello. I should be able to see this body.", 'plain')
    msg.attach(textPart)
    for attachmentFilename in listOfAttachments:
        fp = open(attachmentFilename, 'rb')
        file1 = MIMEBase('application','csv')
        file1.set_payload(fp.read())
        fp.close()
        encoders.encode_base64(file1)
        file1.add_header('Content-Disposition','attachment;filename=%s' % attachmentFilename)
        msg.attach(file1)

    server = smtplib.SMTP(SMTPSERVER)
    server.ehlo()
    server.starttls()
    server.login(FROM, MYEMAILPASSWORD)
    server.sendmail(FROM, to, msg.as_string())
    server.quit()
    return True


physicallySend(["testfile.csv"])

虽然我在Gmail和Outlook上能正常看到邮件的正文,但在我的iPhone(6.1.3)上,我只看到附件,正文却看不到。

2 个回答

2

对于那些想要找到完整解决方案的人,这个可以在所有邮件客户端上正常工作,包括iOS默认邮件应用

什么时候使用 multipart/mixed

来自RFC的说明

主要的子类型“mixed”是用来处理那些各部分内容相互独立并且需要依次显示的情况。任何实现不认识的多部分子类型都应该被视为“mixed”类型。

所以,当消息的所有部分(text/htmltext/plainimage/png等)都是同样重要,并且都应该展示给用户时,就应该使用multipart/mixed类型。

这意味着,如果你在消息中附加了text/html,同时又附加了text/plain作为HTML的备用格式,那么这两者会一个接一个地展示给用户,这样做是没有意义的。

什么时候使用 multipart/alternative

来自RFC的说明

特别是,每个部分都是相同信息的“替代”版本。用户代理应该识别这些部分的内容是可以互换的。用户代理应该根据用户的环境和偏好选择“最佳”类型,或者提供可用的替代选项。一般来说,选择最佳类型意味着只显示最后一个可以展示的部分。这可以用来发送邮件,以一种华丽的文本格式,使其可以在任何地方轻松显示。

这意味着你附加到multipart/alternative消息中的任何内容都可以被视为相同的值,只是表现形式不同。

这为什么重要?

如果在文本部分之后,你在multipart/alternative消息中附加了一张图片(image/pngimage/jpeg),那么这张图片会被视为和消息本身同样重要,因此用户看到的只有图片,而不会看到text/htmltext/plain部分。
不过,这在现在的大多数客户端中并不成立——它们足够聪明,知道你的意思,但仍然这样做的客户端是iOS默认邮件应用——你不想让这些用户感到困惑。

那么正确的做法是什么呢?

经过这些解释,现在会更容易理解了。

# keeps the textual and attachment parts separately
root_msg = MIMEMultipart('mixed')  

root_msg['Subject'] = email.subject
root_msg['From'] = self._format_from_header()
root_msg['To'] = self._format_addrs(recipients=email.recipients)

alter_msg = MIMEMultipart('alternative')

plain_text = MIMEText('some text', 'plain', 'UTF-8')
html = MIMEText('<strong>some text</strong>', 'html')  

alter_msg.attach(plain_text)
alter_msg.attach(html)

# now the alternative message (textual)
# is just a part of the root mixed message
root_msg.attach(alter_msg)

img_path = '...'
filename = os.path.basename(img_path)
with open(img_path, 'rb') as f:
    attachment = MIMEImage(f.read(), filename=filename)          
    attachment.add_header('Content-Disposition', 'attachment', filename=filename)
    # now the images (attachments) will be shown alongside 
    # either plain or html message and not instead of them
    root_msg.attach(attachment)

6

我在这个评论里找到了我的解决办法:如何使用SMTP发送附件?

我的第一行代码应该是

msg = MIMEMultipart('mixed')

而不是'alternative'。

撰写回答