目录结构
2.1.第一步:在【settings.py】里新增邮箱配置信息
2.2.第二步:在【helloworld/hello/views.py】里新增视图函数
2.3.第三步:在【helloworld/helloworld/urls.py】里新增url匹配规则
2.5.第五步:任一浏览器上输入url地址【http://192.168.1.81:8000/send_email_004/】进行访问后,查看结果
3.1.类EmailMultiAlternatives里的方法【__init__】的源码简单分析
3.2.类EmailMultiAlternatives里的方法【attach_file】的源码简单分析
3.3.类EmailMultiAlternatives里的方法【attach】的源码简单分析
1.写这篇博客的目的
主要记录如何通过django来实现这个功能:发送满足【邮件正文值为一个html页面+有多个附件】的单个邮件;
发送满足【邮件正文值为一个html页面+有多个附件】的单个邮件,可以使用类EmailMultiAlternative;
类EmailMultiAlternative提供了三个方法:attach_file()、attach_alternative()、send(),这三个方法的主要作用分别是:
- attach_file()的主要作用:把指定的一个文件当做邮件附件;
- attach_alternative()的主要作用:使邮件正文为一个指定的html页面;
- send()的主要作用:执行发送邮件的动作;
完整操作流程可以看接下来的内容;
2.完整操作流程
2.1.第一步:在【settings.py】里新增邮箱配置信息
# 下面的代码都是我个人新增的,不是djano框架默认生成的; # 1.1.配置qq邮箱信息 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 值必须为这个 EMAIL_USE_SSL = True # SSL加密方式,值必须为True EMAIL_HOST = 'smtp.qq.com' # 发送邮件的qq邮箱的SMTP服务器 EMAIL_PORT = 465 # QQ邮箱对应的SMTP服务器端口 EMAIL_HOST_USER = '727803257@qq.com' # 发件人 EMAIL_HOST_PASSWORD = 'dwvvnvjerwcvswp' # qq授权码(不能使用qq密码只能使用qq授权码) EMAIL_FROM = '洪景盛<727803257@qq.com>' # 邮件显示的发件人
2.2.第二步:在【helloworld/hello/views.py】里新增视图函数
def send_email_004(request): '''发送【邮件正文值为一个html页面+有多个邮件附件】的单个邮件''' email = EmailMultiAlternatives( subject = "这是邮件的邮件标题(发送【邮件正文值为一个html页面+有多个邮件附件】的单个邮件)", body = "这是邮件的正文", from_email = "727803257@qq.com", # 发件人 to = ["727803257@qq.com","3053749194@qq.com"],# 收件人邮箱可以是任意的不限个数的邮箱 ) root_route= os.path.dirname(os.path.realpath(__file__)) # 应用【hello】的根目录 # print(root_route) # 邮件附件里的第一个附件为:一个html页面【这是html页面噢.html】 route_1 = os.path.join(root_route,"public","html","test","这是html页面噢.html") email.attach_file(path=route_1) # 邮件附件里的第二个附件为:一个pdf【202101持续测试白皮书v1.0-Lite.pdf】 route_2 = os.path.join(root_route,"public","pdf","test","202101持续测试白皮书v1.0-Lite.pdf") email.attach_file(path=route_2) # 邮件附件里的第三个附件为:一个图片【1.jpg】 route_3 = os.path.join(root_route,"public","picture","test","1.jpg") email.attach_file(path=route_3) # 邮件附件里的第四个附件为:一个txt【这是txt文档哦.txt】 route_4 = os.path.join(root_route,"public","txt","test","这是txt文档哦.txt") email.attach_file(path=route_4) # 邮件附件里的第五个附件为:一个视频【从21楼拍的海边风景视频.mp4】 route_5 = os.path.join(root_route,"public","video","test","从21楼拍的海边风景视频.mp4") email.attach_file(path=route_5) # 邮件附件里的第六个附件为:一个word【这是word文档噢.docx】 route_6 = os.path.join(root_route,"public","word","test","这是word文档噢.docx") email.attach_file(path=route_6) # 邮件附件里的第七个附件为:一个图片【2.jpg】 # route_7 = os.path.join(root_route,"public","picture","test","2.jpg") # img_data = open(route_7,"rb") # email.attach(filename="2.jpg",content=img_data.read(),mimetype="image/jpg") # attach方法使用起来比较复杂,attach_file方法使用起来很方便,所以后续都只使用attach_file方法; html_content = ''' <!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>带图片的邮件</title> </head> <body> 这是第一张图片,点击图片后可跳转到我的博客首页<br> <a href="https://www.cnblogs.com/xiamen-momo/" target="_blank"> <p> <img src="http://www.w3school.com.cn/i/eg_chinarose.jpg" height="100" width="200" /> </p> </a> <p> 这是第二张图片,点击后不会触发任何跳转效果<br> <img src="http://www.w3school.com.cn/i/eg_chinarose.jpg" height=200 width=400 /> </p> </body> </html> ''' email.attach_alternative(content=html_content,mimetype="text/html")
# 1.当使用了attach_alternative方法,EmailMultiAlternatives类里的初始化入参body的值就不会生效了,即邮件正文为指定的一个html页面;
# 2.当没使用attach_alternative方法,EmailMultiAlternatives类里的初始化入参body的值就会生效了,即邮件正文为指定的body值(body值的数据类型是字符串);
email.send()
return HttpResponse("【邮件正文值为一个html页面+有多个邮件附件】的单个邮件,发送成功啦!")
2.3.第三步:在【helloworld/helloworld/urls.py】里新增url匹配规则
url(r"^send_email_004/$",views.send_email_004),
2.4.第四步:重启服务
2.5.第五步:任一浏览器上输入url地址【http://192.168.1.81:8000/send_email_004/】进行访问后,查看结果
3.相关知识点
3.1.类EmailMultiAlternatives里的方法【__init__】的源码简单分析
class EmailMultiAlternatives(EmailMessage): """ A version of EmailMessage that makes it easy to send multipart/alternative messages. For example, including text and HTML versions of the text is made easier. """ alternative_subtype = 'alternative' def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, connection=None, attachments=None, headers=None, alternatives=None, cc=None, reply_to=None): """ Initialize a single email message (which can be sent to multiple recipients). """ super().__init__( subject, body, from_email, to, bcc, connection, attachments, headers, cc, reply_to, ) self.alternatives = alternatives or []
方法【__init__】里的几个主要入参的分析:
- subject: (必填,数据类型为字符串)邮件标题;
- body: (必填,数据类型为字符串)邮件正文;
- from_email: (非必填,数据类型为字符串)发件邮箱;
- to: (非必填,数据类型为列表)列表中每个值都是一个接收邮件的邮箱地址;
3.2.类EmailMultiAlternatives里的方法【attach_file】的源码简单分析
def attach_file(self, path, mimetype=None): """ Attach a file from the filesystem. Set the mimetype to DEFAULT_ATTACHMENT_MIME_TYPE if it isn't specified and cannot be guessed. For a text/* mimetype (guessed or specified), decode the file's content as UTF-8. If that fails, set the mimetype to DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content. """ path = Path(path) with path.open('rb') as file: content = file.read() self.attach(path.name, content, mimetype)
从方法【attach_file】的源码可以大概看出来,其实方法【attach_file】是对方法【attach】的进一步封装;
3.3.类EmailMultiAlternatives里的方法【attach】的源码简单分析
def attach(self, filename=None, content=None, mimetype=None): """ Attach a file with the given filename and content. The filename can be omitted and the mimetype is guessed, if not provided. If the first parameter is a MIMEBase subclass, insert it directly into the resulting message attachments. For a text/* mimetype (guessed or specified), when a bytes object is specified as content, decode it as UTF-8. If that fails, set the mimetype to DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content. """ if isinstance(filename, MIMEBase): assert content is None assert mimetype is None self.attachments.append(filename) else: assert content is not None mimetype = mimetype or mimetypes.guess_type(filename)[0] or DEFAULT_ATTACHMENT_MIME_TYPE basetype, subtype = mimetype.split('/', 1) if basetype == 'text': if isinstance(content, bytes): try: content = content.decode() except UnicodeDecodeError: # If mimetype suggests the file is text but it's # actually binary, read() raises a UnicodeDecodeError. mimetype = DEFAULT_ATTACHMENT_MIME_TYPE self.attachments.append((filename, content, mimetype))
截取这部分源码:
mimetype = mimetype or mimetypes.guess_type(filename)[0] or DEFAULT_ATTACHMENT_MIME_TYPE basetype, subtype = mimetype.split('/', 1)
从这部分源码可以简单看出来:当入参mimetype值为None,django会自动根据附件文件名来推测MIME内容类型的值并赋值给入参mimetype,然后入参mimetype的新值做后续相关处理;
3.4.每个应用的资源存放位置
什么是资源?比如:一个图片、一个html页面、一个css、一个js、一个视频、一个pdf、一个word、一个txt,等等这些都是不同的资源类型;
每个项目的应用都有对应所属的资源,那么每个应用的资源存放位置要怎么定呢?怎么定的思路,个人做法主要是这个思路步骤:
- 第一步:每个应用的资源存放位置定为在每个应用根目录下的某个手动新增的文件夹,比如该新增的文件夹名为【public】;
- 第二步:在文件夹【public】里按不同的资源类型新增对应不同的文件夹,比如新增的文件夹名为【css】和【word】;
- 第三步:在文件夹【word】里按不同的数据表/按不同的业务模块新增对应不同的文件夹,比如新增的文件夹名为【user】和【test】;
这样的思路步骤的主要目的是:每个应用的资源在各自应用目录里维护,大大减少项目代码的耦合度,方便后续维护;
以一个应用【hello】为例,应用【hello】的资源存放位置所对应的具体的文件层级结构大概如下: