向用户发送邮件是Web应用中最常见的任务之一,例如用户注册、找回密码等。Python内置了一个smtplib模块,可用于发电子邮件。这里我们使用Flask-Mail,因为它可以和Flask集成,方便我们实现这个功能。安装使用pip安装:$pipinstallFlask-Mail或者下载源码安装:$gitclonehttps://github.com/mattupstate/flask-mail.git$cdflask-mail$pythonsetup.pyinstall发送邮件Flask-Mail连接SimpleMailTransferProtocol(SimpleMailTransferProtocol,SMTP)服务器,并将邮件发送到该服务器进行发送。下面我们以QQ邮箱为例,介绍一下如何轻松发送邮件。在此之前,我们需要知道QQ邮箱的服务器地址和端口是多少,点此查看。#-*-coding:utf-8-*-fromflaskimportFlaskfromflask_mailimportMail,Messageimportosapp=Flask(__name__)app.config['MAIL_SERVER']='smtp.qq.com'#邮件服务器地址app.config['MAIL_PORT']=25#邮件服务器端口app.config['MAIL_USE_TLS']=True#启用TLSapp.config['MAIL_USERNAME']=os.environ.get('MAIL_USERNAME')or'me@example.com'app.config['MAIL_PASSWORD']=os.environ.get('MAIL_PASSWORD')or'123456'mail=Mail(app)@app.route('/')defindex():msg=Message('Hi',sender='me@example.com',recipients=['he@example.com'])msg.html='HelloWeb'#msg.body='Thefirstemail!'mail.send(msg)return'
OK!
'if__name__=='__main__':app.run(host='127.0.0.1',debug=True)发送前需要先设置用户名和密码,当然也可以直接写入文件,如果是从环境变量中读取,可以这样:$exportMAIL_USERNAME='me@example.com'$exportMAIL_PASSWORD='123456'把上面的发件人和收件人改一下,就可以测试了。从上面的代码我们可以知道,使用Flask-Mail发送邮件主要有以下步骤:配置app对象的邮件服务器地址、端口、用户名和密码创建一个Mail实例:mail=Mail(app)创建一个Message消息实例,带有三个参数:邮件标题,发件人和收件人创建邮件内容,如果是HTML格式,使用msg.html,如果是纯文本格式,使用msg.body最后调用mail.send(msg)发送消息Flask-Mail配置项Flask-Mail使用标准的Flask配置API进行配置,以下是一些常用的配置项:配置项说明MAIL_SERVER邮件服务器地址,默认为localhostMAIL_PORT邮件服务器端口,默认为25MAIL_USE_TLS是否发送启用传输安全传输层(TLS)协议,默认为FalseMAIL_USE_SSL是否启用安全套接字层(SecureSocketsLayer,SSL)协议,默认为FalseMAIL_DEBUG是否启用DEBUG,默认为app.debugMAIL_USERNAME邮件服务器用户名,默认为NoneMAIL_PASSWORD邮件服务器密码,默认为NoneMAIL_DEFAULT_SENDER邮件发送people,默认是None,也可以在Message对象中指定MAIL_MAX_EMAILS的上限。默认值为无。用上面的方式发送邮件,你会发现在消息出现前页面会卡顿几秒,这是因为我们使用了同步方式为了避免发送邮件延迟,我们把发送邮件的任务移到后台线程,代码如下:#-*-coding:utf-8-*-fromflaskimportFlaskfromflask_mailimportMail,MessagefromthreadingimportThreadimportosapp=Flask(__name__)app.config['MAIL_SERVER']='smtp.qq.com'app.config['MAIL_PORT']=25app.config['MAIL_USE_TLS']=Trueapp.config['MAIL_USERNAME']=os.environ.get('MAIL_USERNAME')或'smtp.example.com'app.config['MAIL_PASSWORD']=os.environ.get('MAIL_PASSWORD')或'123456'mail=Mail(app)defsend_async_email(app,msg):withapp.app_context():邮件。send(msg)@app.route('/sync')defsend_email():msg=Message('Hi',sender='me@example.com',recipients=['he@example.com'])msg.html='
异步发送邮件'thr=Thread(target=send_async_email,args=[app,msg])thr.start()return'sendsuccessfully'if__name__=='__main__':app.run(host='127.0.0.1',debug=True)上面我们创建了一个线程,执行的任务是send_async_email,这个任务的执行涉及到一个问题1:很多Flask扩展都假设已经有一个活跃的程序上下文和请求上下文。Flask-Mail中的send()函数使用了current_app,因此必须激活程序上下文。但是,当mail.send()函数在不同的线程中执行时,程序上下文是使用app.app_context()手动创建的。带附件的邮件有时候,我们在发送邮件的时候,需要添加附件,比如文档、图片等,这也很简单。代码如下:#-*-coding:utf-8-*-fromflaskimportFlaskfromflask_mailimportMail,Messageimportosapp=Flask(__name__)app.config['MAIL_SERVER']='smtp.qq.com'#邮件服务器地址app.config['MAIL_PORT']=25#邮件服务器端口app.config['MAIL_USE_TLS']=True#启用TLSapp.config['MAIL_USERNAME']=os.environ.get('MAIL_USERNAME')or'me@example.com'app.config['MAIL_PASSWORD']=os.environ.get('MAIL_PASSWORD')or'123456'mail=Mail(app)@app.route('/attach')defadd_attchments():msg=Message('嗨',sender='me@example.com',recipients=['other@example.com'])msg.html='
HelloWeb'withapp.open_resource("/Users/Admin/Documents/pixel-example.jpg")asfp:msg.attach("photo.jpg","image/jpeg",fp.read())mail.send(msg)return'
OK!
'if__name__=='__main__':app.run(host='127.0.0.1',debug=True)在上面的代码中,我们通过app.open_resource(path_of_attachment)打开一张机器的图片,然后在Message中添加附件内容对象通过msg.attach()方法。msg.attach()方法的第一个参数是附件的文件名,第二个参数是文件内容的MIME(MultipurposeInternetMailExtensions)类型,第三个参数是文件内容。如果您不知道附件的MIME类型是什么,可以查看MIME参考手册。批量发送在某些情况下,我们需要批量发送邮件,比如向网站所有注册用户发送修改密码的邮件。这个时候,为了避免每次发送邮件都会创建和关闭与服务器的连接,我们的代码需要做一些调整,类似如下:withmail.connect()asconn:foruserinusers:subject="hello,%s"上面的%user.namemsg=Message(recipients=[user.email],body='...',subject=subject)conn.send(msg)可以使应用程序保持与电子邮件服务器的连接,直到所有电子邮件都已发送发送。有些邮件服务器会限制一次连接发送邮件的上限,所以可以配置MAIL_MAX_EMAILS。应该注意的是,发送大量电子邮件的更好方法是使用专用操作系统,例如Celery任务队列。本文的完整代码在这里。