python 发送邮件,附件中文命名,怎么破

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python 发送邮件,附件中文命名,怎么破相关的知识,希望对你有一定的参考价值。

不知道你是不是用的smtp来发的,我的发中文的附件没问题

#coding=utf-8
\'\'\'
Created on 2014-11-03

@author: Neo
\'\'\'
import smtplib
from email.mime.text import MIMEText
import email.mime.multipart
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email import Encoders

#你自己修改下xxx的部分
mailto_list = [\'xxx@xxx.com\']
mail_host = "smtp.xxx.com" # 设置服务器
mail_user = "xxxx" # 用户名
mail_pass = "xxxxx" # 口令
mail_postfix = "xxxx.com" # 发件箱的后缀
def send_mail():
me = "Hello" + "<" + mail_user + "@" + mail_postfix + ">" # 这里的hello可以任意设置,收到信后,将按照设置显示

content = \'Plz get the attachment!\'
msg = MIMEMultipart()
body = MIMEText(content, _subtype=\'html\', _charset=\'gb2312\') # 创建一个实例,这里设置为html格式邮件
msg.attach(body)
msg[\'Subject\'] = "Test" # 设置主题
msg[\'From\'] = me
msg[\'To\'] = ";".join(mailto_list)

part = MIMEBase(\'application\', \'octet-stream\')
# 读入文件内容并格式化,此处文件为当前目录下,也可指定目录 例如:open(r\'/tmp/123.txt\',\'rb\')
part.set_payload(open(u\'test中文.txt\',\'rb\').read())
Encoders.encode_base64(part)
## 设置附件头
part.add_header(\'Content-Disposition\', u\'attachment; filename="test中文.txt"\')
msg.attach(part)

try:
s = smtplib.SMTP()
s.connect(mail_host) # 连接smtp服务器
s.login(mail_user, mail_pass) # 登陆服务器
s.sendmail(me, mailto_list, msg.as_string()) # 发送邮件
s.close()
print \'send mail sucess\'
return True
except Exception, e:
print str(e)
return False

send_mail()

测试,发送ok
参考技术A from email.header import make_header
file_msg = MIMEText(open(file,'rb').read(), 'base64', 'UTF-8')
file_msg["Content-Type"] = 'application/octet-stream;name="%s"'% make_header([(file,'UTF-8')]).encode('UTF-8')
file_msg["Content-Disposition"] = 'attachment;filename= "%s"' % make_header([(file, 'UTF-8')]).encode('UTF-8')
msg.attach(file_msg)

当我有来自的命名附件时,Outlook 365 不接受 Python 电子邮件消息库输出

【中文标题】当我有来自的命名附件时,Outlook 365 不接受 Python 电子邮件消息库输出【英文标题】:Python's Email Message library output not getting accepted by Outlook 365 when i have a named attachments from 【发布时间】:2019-02-18 17:43:40 【问题描述】:

我创建了一个示例函数来测试发送带有附加 html 文件的电子邮件,我打算用它来报告未来的自动化测试运行(替换现有的外部 powershell 脚本)。请注意,我附加的是 html 文件,而不是使用 html 作为正文中的内联文本。我正在使用我们公司的 mailgun smtp 帐户服务发送电子邮件。

我似乎遇到了 Outlook 365(网络托管 - 使用 Outlook.office.com 域)拒绝或阻止发送的电子邮件的问题,但有趣的是,我的个人 hotmail 地址(outlook. live.com 域)。当我尝试在电子邮件对象中命名文件时,我发现 Outlook 365 阻止或不接受电子邮件。但如果我不命名它,它会通过(默认名称为 "ATT00001.htm" )。

我的代码如下,但它们的关键行似乎是

msg.add_attachment(open_file.read(), maintype='text', subtype='html', filename=filename)

如果我删除文件名键,它可以工作(但使用默认分配的文件名)例如

msg.add_attachment(open_file.read(), maintype='text', subtype='html')

我怀疑附件的标题或内容处置中存在 Outlook 365 不同意的内容,但我不确定它是什么或如何解决。

我正在使用以下(Python 3.6.5,在 Windows 10 机器上,似乎内置了 smtplib 和 email.message)

代码如下:

import smtplib
from email.message import EmailMessage
import os


def send_mail():
    MAILGUN_SMTP_LOGIN = "<my company's mailgun login>"
    MAILGUN_SMTP_PASSWORD = "<my company's mailgun password>"

    fromaddr = "muppet@sharklasers.com" # the from address seems to be inconsequential 
    toaddr = ['me@mycompanysdomainusingoffice365.com.au', 'me@hotmail.com']

    msg = EmailMessage()
    msg.preamble = 'This is preamble. Not sure where it should show in the email'

    msg['From'] = fromaddr
    msg['To'] = ', '.join(toaddr)
    msg['Subject'] = 'Testing attached html results send'
    msg.set_content(""" This is a test of attached html """)


    filename = 'api_automatedtests_20180903_1341.html'
    filepath = os.path.abspath('D:/work/temp/api_automatedtests_20180903_1341.html')
    open_file = open(filepath, "rb")
    # msg.make_mixed()
    msg.add_attachment(open_file.read(), maintype='text', subtype='html', filename=filename)
    # msg.add_attachment(open_file.read(), maintype='text', subtype='html')


    server = smtplib.SMTP(host="smtp.mailgun.org", port=587)
    server.ehlo()
    server.starttls()
    server.login(MAILGUN_SMTP_LOGIN, MAILGUN_SMTP_PASSWORD)
    server.set_debuglevel(1)
    server.send_message(msg)
    server.quit()


if __name__ == "__main__":
    send_mail()

我尝试过的

    尝试使用文本文件(具有适当的类型)使用相同的代码发送。例如msg.add_attachment(open_file.read(), maintype='text', subtype='plain', filename=filename) 结果:这按预期工作(通过给定名称出现 - 文件名是字符串变量,例如 testfile.txt)

    添加 msg.make_mixed() 以确保它被识别为多部分消息。结果:没有效果

    打开 smtp 调试级别 1,结果:Mailgun 说一切正常(并且消息确实按预期显示在我的 hotmail 帐户中)

    在 msg.add_attachment 调用中未使用文件名键。 结果:这个工作附件来自 ATT00001.htm 有趣的是,默认名称是 *.htm 而我尝试使用的文件名是 *.html

      尝试使用带有 *.htm 的文件名和“htm”子类型(而不是 html) 结果:与 html 相同(在 hotmail 上收到但在 Outlook 365 上没有收到)

      尝试使用泛型类型 maintype=''application', subtype='octet-stream'。 例如msg.add_attachment(open_file.read(), maintype='application', subtype='octet-stream', filename=filename) 结果:与 html 相同(在 hotmail 上收到但在 Outlook 365 上没有收到)

      尝试使用 mimetypes.guess,如此链接所示https://docs.python.org/3.6/library/email.examples.html

    ctype, encoding = mimetypes.guess_type(path) 如果 ctype 为 None 或 encoding 不是 None: # 无法猜测,或者文件被编码(压缩),所以 # 使用通用的 bag-of-bits 类型。 ctype = '应用程序/八位字节流' maintype, subtype = ctype.split('/', 1) 使用 open(path, 'rb') 作为 fp: msg.add_attachment(fp.read(), 主类型=主类型, 子类型=子类型, 文件名=文件名) 结果:确定为maintype='text', subtype='html',我得到的结果与我原来的代码相同(即到达hotmail但被365阻止)。

      检查我的垃圾邮件和杂乱文件夹 - 不在那里

关于为什么使用文件名会破坏它的任何建议?

更新 在通过不同的提供商发送到其他电子邮件地址后,我发现:

1) muppet@sharklasers.com 不是受信任的发件人(可以更改)

2) 我发现附件被标记为不安全。 html 文件来自 pytest 的带有单个文件选项的 html 报告。它包含用于行扩展器的 javascript。 Gmail 警告附件可能不安全(office 365 直接完全屏蔽了电子邮件)。

不确定如何解决 2)。我可以在 Outlook 365 和 gmail 之间通过电子邮件将相同的文件发送给自己,反之亦然,并且文件不会被阻止。只有当我使用 python 库和 Mailgun SMTP 使用上述脚本时,它才会被阻止。我怀疑我需要在电子邮件标题中更改某些内容才能解决此问题。但我不知道是什么。

在尝试添加文件名和附件被标记为不安全之间似乎存在某种联系

【问题讨论】:

【参考方案1】:

好的,我想通了。问题是需要在其值中包含“name=filename”的内容类型。 我还需要使用 maintype='multipart', subtype='mixed'。

我有 2 个解决方案。

解决方案 1

import smtplib
from email.message import EmailMessage
import os

def send_mail(body_text, fromaddr, recipient_list, smtp_login, smtp_pass, file_path):
    msg = EmailMessage()
    msg.preamble = 'This is preamble. Not sure where it should show'

    msg['From'] = fromaddr
    msg['To'] = ', '.join(recipient_list)
    msg['Subject'] = 'API Testing results'
    msg.set_content(body_text)

    filename = os.path.basename(file_path)
    open_file = open(file_path, "rb")
    msg.add_attachment(open_file.read(), maintype='multipart', subtype='mixed; name=%s' % filename, filename=filename)

    server = smtplib.SMTP(host="smtp.mailgun.org", port=587)
    server.ehlo()
    server.starttls()
    server.login(smtp_login, smtp_pass)
    server.send_message(msg)
    server.quit()


if __name__ == "__main__":
    smtp_login = "<my smtp login>"
    smtp_pass = "<my smtp password>"
    recipient_list = ['user1@mycompany.com.au', 'user2@mycompany.com.au']
    file_path = os.path.abspath('D:/work/temp/api_automatedtests_20180903_1341.html')
    body_text = "test results for 03/09/2018 "
    fromaddr = 'autotesting@mycompany.com.au'
    send_mail(body_text=body_text, recipient_list=recipient_list, smtp_login=smtp_login, smtp_pass=smtp_pass,
              file_path=file_path)

解决方案 2(根据使用 email.mime 库的文档,这是一个遗留解决方案,应该优先使用 EmailMessage 方法。

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import os

def send_mail(body_text, fromaddr, recipient_list, smtp_login, smtp_pass, file_path):

    msg = MIMEMultipart()
    msg['From'] = fromaddr
    msg['To'] = ', '.join(recipient_list)
    msg['Subject'] = "Sending API test results"
    msg.attach(MIMEText(body_text, 'plain'))

    filename = os.path.basename(file_path)
    attachment = open(file_path, "rb")

    part = MIMEBase('multipart', 'mixed; name=%s' % filename)
    part.set_payload(attachment.read())
    encoders.encode_base64(part)
    part.add_header('Content-Disposition', "attachment; filename= %s" % filename)

    msg.attach(part)

    server = smtplib.SMTP(host="smtp.mailgun.org", port=587)
    server.starttls()
    server.login(smtp_login, smtp_pass)
    text = msg.as_string()
    server.set_debuglevel(1)
    server.sendmail(fromaddr, recipient_list, text)
    server.quit()

if __name__ == '__main__':
    smtp_login = "<my smtp login>"
    smtp_pass = "<my smtp password>"
    recipient_list = ['user1@mycompany.com.au', 'user2@mycompany.com.au']
    file_path = os.path.abspath('D:/work/temp/api_automatedtests_20180903_1341.html')
    body_text = " Api test results for 03/09/2018 "
    fromaddr = "autotest@mycompany.com.au"
    send_mail(body_text=body_text, fromaddr=fromaddr, recipient_list=recipient_list, smtp_login=smtp_login, smtp_pass=smtp_pass,
              file_path=file_path)

【讨论】:

以上是关于python 发送邮件,附件中文命名,怎么破的主要内容,如果未能解决你的问题,请参考以下文章

Python向多人发送、抄送带附件的邮件(含详细代码)

如何仅使用电子邮件应用程序发送带附件的电子邮件?

基于Python实现邮件发送

python 发送带附件的邮件,收到的附件名都变成了tcmime.1774.1903.2076.bin格式,怎么回事呢?

用python调用outlook发邮件的问题

Python SMTP 发送带附件电子邮件