[英]EmailMessage doesn't show correctly in the sent emails with python's email and smtplib packages
My emails can be correctly sent but don't show correctly in the receiver mails.我的电子邮件可以正确发送,但在接收方邮件中显示不正确。 It looks like this:它看起来像这样:
To: =?utf-8?b?..?= <....com> MIME-Version: 1.0 Content-Type: multipart/mixed; To: =?utf-8?b?..?= <....com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============5404281335870522242=="边界="================5404281335870522242=="
--===============5404281335870522242== Content-Type: text/plain; --==============5404281335870522242== 内容类型:文本/普通; charset="utf-8" Content-Transfer-Encoding: base64 charset="utf-8" 内容传输编码:base64
5bCK5pWs55qE5a2U6LaF5YW... 5bCK5pWs55qE5a2U6LaF5YW...
--===============5404281335870522242== Content-Type: image/png Content-Transfer-Encoding: base64 Content-Disposition: attachment; --================5404281335870522242== 内容类型:图像/png 内容传输编码:base64 内容处理:附件; filename="user.png" MIME-Version: 1.0文件名 =“user.png” MIME 版本:1.0
iVBORw0KGgo... iVBORw0KGgo...
The MIME string is directly shown except the Subject
and the From
line(It is shown after the To
) as well as all bodies in plain text. MIME 字符串直接显示,除了Subject
和From
行(显示在To
)以及纯文本中的所有正文。
There's my code:这是我的代码:
import smtplib
import ssl
import mimetypes
from pathlib import Path
from email.message import EmailMessage
from email.utils import formataddr
import time
class EmailSender:
PORT = 465
CONTEXT = ssl.create_default_context()
def __init__(
self,
username,
password,
host,
):
self.username = username
self.password = password
self.host = host
self.mails = []
def _add_name_header(self, name="", mail_addr=""):
if name:
return formataddr((name, mail_addr))
else:
return mail_addr
def add_mail(
self,
from_email="",
from_name="",
to_email="",
to_name="",
subject="",
message_txt="",
files=None,
):
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = self._add_name_header(from_name, from_email)
msg["To"] = self._add_name_header(to_name, to_email)
msg.set_content(message_txt)
if not files is None:
for file_obj in files:
if file_obj.exists():
file = str(file_obj)
ctype, encoding = mimetypes.guess_type(file)
if ctype is None or encoding is not None:
# No guess could be made, or the file is encoded (compressed), so use a generic bag-of-bits type.
ctype = "application/octet-stream"
maintype, subtype = ctype.split("/", 1)
with file_obj.open("rb") as fp:
msg.add_attachment(
fp.read(),
maintype=maintype,
subtype=subtype,
filename=file_obj.name,
)
self.mails.append(msg)
def send(self, time_interval=1):
with smtplib.SMTP_SSL(
host=self.host, port=self.PORT, context=self.CONTEXT
) as server:
try:
server.login(user=self.username, password=self.password)
except Exception as e:
# Need process errors
raise e
for msg in self.mails:
server.send_message(msg)
time.sleep(time_interval)
And I just do:我只是这样做:
sender = EmailSender(
username, password, host="smtp.163.com"
)
files = list(Path("D:/").glob("*.pdf"))
sender.add_mail(
from_email, from_name, to_email, to_name, subject, message_txt, files=None
)
sender.send(time_interval=10)
I'm the OP of the question.我是问题的 OP。 I just solved this problem by myself and I'd share the solution.我只是自己解决了这个问题,我会分享解决方案。
TLNR: Non-Ascii chars are used in my mails so use msg = EmailMessage(EmailPolicy(utf8=True))
instead of msg = EmailMessage()
. TLNR:我的邮件中使用了非 Ascii 字符,因此请使用msg = EmailMessage(EmailPolicy(utf8=True))
而不是msg = EmailMessage()
。
I misunderstood these sentences in the doc of SMTP.send_message
:我误解了SMTP.send_message
文档中的这些句子:
If any of the addresses in from_addr and to_addrs contain non-ASCII characters and the server does not advertise SMTPUTF8 support, an SMTPNotSupported error is raised.如果 from_addr 和 to_addrs 中的任何地址包含非 ASCII 字符并且服务器不通告 SMTPUTF8 支持,则会引发 SMTPNotSupported 错误。 Otherwise the Message is serialized with a clone of its policy with the utf8 attribute set to True, and SMTPUTF8 and BODY=8BITMIME are added to mail_options.否则,将使用 utf8 属性设置为 True 的策略克隆序列化消息,并将 SMTPUTF8 和 BODY=8BITMIME 添加到 mail_options。
Since I add a non-ASCII header to my address, I believe that smtplib
will automatically use the utf8 policy for me.由于我在地址中添加了非 ASCII 标头,因此我相信smtplib
会自动为我使用 utf8 策略。 But in the file smtplib.py
I saw this:但是在文件smtplib.py
我看到了这个:
if from_addr is None:
# Some code
from_addr = email.utils.getaddresses([from_addr])[0][1]
if to_addrs is None:
# Some code
to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
# Some code
international = False
try:
"".join([from_addr, *to_addrs]).encode("ascii")
except UnicodeEncodeError:
# Some code
international = True
That is, the function only checks if the address parts have non-ASCII chars but not along with the header names.也就是说,该函数仅检查地址部分是否具有非 ASCII 字符,但不检查标题名称。
After that, the message is dealt with as pure ASCII content, that's maybe no problem, I have no idea why, but somohow, many extra /r
chars are inserted before and after the To:xxx
line , which makes the smtp server think this as a separator maybe?之后,消息被处理为纯 ASCII 内容,这可能没问题,我不知道为什么,但是somohow,在To:xxx
行之前和之后插入了许多额外的/r
字符,这使得 smtp 服务器认为这一点也许作为分隔符? And finally caused the problem.并最终导致了问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.