当我有来自的命名附件时,Outlook 365 不接受 Python 电子邮件消息库输出
Posted
技术标签:
【中文标题】当我有来自的命名附件时,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)
【讨论】:
以上是关于当我有来自的命名附件时,Outlook 365 不接受 Python 电子邮件消息库输出的主要内容,如果未能解决你的问题,请参考以下文章
Android后台服务无法解析/访问outlook.office365.com,除非手机负责?
在文件名上使用 ReceivedTime 保存来自 Outlook 的附件