Python 的电子邮件消息库输出未被 Outlook 365 接受,当我有来自
Python's Email Message library output not getting accepted by Outlook 365 when i have a named attachments from
我创建了一个示例函数来测试发送带有附加 html 文件的电子邮件,我打算将其用于报告未来的自动测试运行(替换现有的外部 powershell 脚本)。请注意,我附加的是 html 文件,而不是使用 html 作为 body 中的内联文本。我正在使用我们公司的mailgun smtp帐户服务来发送邮件。
我似乎遇到了 Outlook 365(网络托管 - 使用 outlook.office.com 域)拒绝或阻止发送的电子邮件的问题,但有趣的是,我的个人 hotmail 地址收到并接受了同一封电子邮件( outlook.live.com 域)。当我尝试在电子邮件 object 中命名文件时,我发现 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 不同意附件的 header 或 Content-disposition 中的某些内容,但我不确定它是什么或如何解决。
我正在使用以下内容(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,如此 link
所示
https://docs.python.org/3.6/library/email.examples.html
ctype, 编码 = mimetypes.guess_type(路径)
如果 ctype 是 None 或编码不是 None:
# 无法猜测,或者文件被编码(压缩),所以
# 使用通用 bag-of-bits 类型。
ctype = 'application/octet-stream'
主类型,子类型 = ctype.split('/', 1)
打开(路径,'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 使用上述脚本时,它才会被阻止。我怀疑我需要在电子邮件 header 中更改一些内容以解决此问题。但是我不知道是什么。
尝试添加文件名和附件被标记为不安全之间似乎存在某种联系
好的,我明白了。问题是需要在其值中包含 "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)
我创建了一个示例函数来测试发送带有附加 html 文件的电子邮件,我打算将其用于报告未来的自动测试运行(替换现有的外部 powershell 脚本)。请注意,我附加的是 html 文件,而不是使用 html 作为 body 中的内联文本。我正在使用我们公司的mailgun smtp帐户服务来发送邮件。
我似乎遇到了 Outlook 365(网络托管 - 使用 outlook.office.com 域)拒绝或阻止发送的电子邮件的问题,但有趣的是,我的个人 hotmail 地址收到并接受了同一封电子邮件( outlook.live.com 域)。当我尝试在电子邮件 object 中命名文件时,我发现 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 不同意附件的 header 或 Content-disposition 中的某些内容,但我不确定它是什么或如何解决。
我正在使用以下内容(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,如此 link
所示 https://docs.python.org/3.6/library/email.examples.html
ctype, 编码 = mimetypes.guess_type(路径) 如果 ctype 是 None 或编码不是 None: # 无法猜测,或者文件被编码(压缩),所以 # 使用通用 bag-of-bits 类型。 ctype = 'application/octet-stream' 主类型,子类型 = ctype.split('/', 1) 打开(路径,'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 使用上述脚本时,它才会被阻止。我怀疑我需要在电子邮件 header 中更改一些内容以解决此问题。但是我不知道是什么。
尝试添加文件名和附件被标记为不安全之间似乎存在某种联系
好的,我明白了。问题是需要在其值中包含 "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)