最近,公司给了我一个需求,将邮件的渲染方式进行优化,主要的话,其实就是改变邮件中图片的展示方式.
之前,公司的邮件是以html的形式发送的,即邮件中的背景图片是通过超链接来实现的,这样就存在一个问题:
如果以后图片的地址发生了改变,那么想再次查看邮件就变得非常不愉快,因为样式都变了.
所以,图片不能再以超链接的形式进行访问,而是需要将图片当做资源进行发送.
老大给我的提示是将整个邮件转换成eml进行发送.
eml是什么?eml是邮件的通用格式,如果我们去查看邮件的源代码,就可以发现,里面的格式并不是我们属性的html,而是经过编码之后的内容,这就是eml格式.
谷歌一番之后,发现找不到html to eml,只有 eml to html ,可能还是我找的方法有问题
一番挣扎之后,决定退而求其次,只考虑将图片当做资源进行发送.
在这之前,我觉得首先要对邮件格式进行一定的了解
https://www.cnblogs.com/wuyoucao/archive/2017/05/19/6880202.html
https://www.cnblogs.com/zhangxinqi/p/9113859.html
整个邮件体就是 MIMEMultipart(),其他所有的内容都通过attach()包含进里面.
发送图片的话,则需要通过MIMEImage().
首先说一下,就我所知,图片的渲染方式,除了通过超链接,还有两种
1.image data url
将图片进行base64编码,将图片编码后的内容直接插入到src中
for filename in os.listdir(static_files_path):
fp = open(static_files_path + filename, 'rb')
# 编码
imageBinary = base64.b64encode(fp.read())
index = filename.rindex(".")
# 将编码后的内容放到字典中,再嵌入到html模板
template_params[filename[:index]] = str(imageBinary)[2:-1]
邮件渲染
#通过 data:image/png;base64 解析
<img src="https://img-blog.csdnimg.cn/2022010700234272350.png" >
类似
<img src="https://img-blog.csdnimg.cn/2022010700234224046.png">
我本来就是这样做的,因为我发现用另一种方法,thunderbird样式会发生错乱,但是老大说,如果图片过大,很有可能会被拦截,邮件展示的话,其实只要兼容大部分网页邮件客户端就行了.
我只好换成下面的方式
- cid
这种方式其实和eml是一样的,给图片资源一个id,然后子在src中通过id去寻找资源,将其渲染出来
for filename in os.listdir(static_files_path):
fp = open(static_files_path + filename, 'rb')
msgImage = MIMEImage(fp.read())
index = filename.rindex(".")
#为图片资源分配一个 cid,名字随意,只有模板中能对应起来就行
msgImage.add_header('Content-ID', filename[:index])
#内嵌资源 Content-Disposition 应该为online,附件则为 attachment
msgImage.add_header('Content-Disposition', 'inline')
template_params[filename[:index]] = filename[:index]
# 将图片加入邮件体
msg.attach(msgImage)
邮件渲染
#通过 cid 寻找
<img src="cid:{{ email_header }}" >
这种方式,像thunderbird这种邮件客户端是不兼容的,图片会以附件形式存在.
以下,附上邮件发送的部分代码,但是是根据公司需求的,仅供我以后查看,同学们如果需要也可以参考一下,可能不是很详细
def get_msg(text=None, files=None, static_files_path=None, text_type='plain',
template=None, template_params=None):
"""
:param text: 邮件内容(text)
:param files: 邮件附件(list)
:param static_files_path: 内嵌图片所在文件夹路径
:param text_type: 邮件内容的类型,plain为文本类型,hmtl为网页类型
:param template: 邮件模板地址,使用django template作为模板引擎
:param template_params:
:return: 邮件模板的参数(dict)
"""
template_params = template_params or {}
try:
if not isinstance(template_params, dict):
raise AssertionError('mail tempalte_params must be dict')
msg = MIMEMultipart()
if static_files_path:
for filename in os.listdir(static_files_path):
fp = open(static_files_path + filename, 'rb')
msgImage = MIMEImage(fp.read())
index = filename.rindex(".")
msgImage.add_header('Content-ID', filename[:index])
msgImage.add_header('Content-Disposition', 'inline')
template_params[filename[:index]] = filename[:index]
msg.attach(msgImage)
if template:
mail_template = get_template(template)
text = mail_template.render(template_params)
msg.attach(MIMEText(text, text_type))
for f in files or []:
with open(f, "rb") as fil:
part = MIMEApplication(
fil.read(),
Name=basename(f)
)
# After the file is closed
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
msg.attach(part)
return msg
except Exception as e:
raise e
def send_mail(send_from, send_to, subject, msg, server_option=None):
"""
https://stackoverflow.com/questions/3362600/how-to-send-email-attachments#answer-3363254
:param send_from: 发件人
:param send_to: 收件人(list)
:param subject: 邮件主题
:param msg: 邮件内容对象(MIMEMultipart)
:param server_option: smtp server配置
server_option = {
'host': '127.0.0.1',
'port': 465,
'user': 'user',
'pass': 'pass',
'ssl': True
}
:return:
"""
server = None
server_option = server_option or {}
host = server_option.get('host', '127.0.0.1')
port = server_option.get('port', '25')
try:
if not isinstance(msg, MIMEMultipart):
raise AssertionError('mail send_to must be MIMEMultipart')
if not isinstance(send_to, list):
raise AssertionError('mail send_to must be list')
msg['From'] = str(send_from)
msg['To'] = COMMASPACE.join(send_to)
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
if server_option.get('ssl', True):
server = smtplib.SMTP_SSL(host=host, port=port)
else:
server = smtplib.SMTP(host=host, port=port)
if server_option.get('user'):
server.login(server_option.get('user'), server_option.get('pass'))
server.sendmail(send_from, send_to, msg.as_string())
except Exception as e:
raise e
finally:
if server:
server.close()