python标准库中内置了邮件支持模块smtplib,但是包装了smtplib的Flask-Mail能更好的和Flask程序集成。
Flask-Mail连接到SMTP(简单邮件传输协议:simple mail transfer protocol)服务器,把邮件交给这个服务器发送。
一、安装和相关配置参数
# pyCharm Terminal
pip install flask-mail
什么是Shell:
-
shell
(计算机壳层)
在计算机科学中,Shell俗称壳(用来区别于核),是指“提供使用者使用界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。
基本上shell分两大类:
一:图形界面shell(Graphical User Interface shell 即 GUI shell)
例如:应用最为广泛的 Windows Explorer (微软的windows系列操作系统),还有也包括广为人知的 Linux shell,其中
linux shell 包括 X window manager (BlackBox和FluxBox),以及功能更强大的CDE、GNOME、KDE、 XFCE。
二:命令行式shell(Command Line Interface shell ,即CLI shell)
因此,在python解释器的命令行就是shell,在pyCharm中python console就是
二、看例子
# hello.py
import os #操作系统模块
from flask import Flask, render_template, session, redirect, url_for #引入Flask类、render_templdate函数,session类等
from flask_bootstrap import Bootstrap #引入Bootstrap类
from flask_moment import Moment
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_mail import Mail, Message
basedir = os.path.abspath(os.path.dirname(__file__)) #当前文件所在的绝对路径
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string' #Flask-wtf需要的配置
app.config['SQLALCHEMY_DATABASE_URI'] =\ #Flask-SQLAlchemy需要的配置
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False #Flask-SQLAlchemy需要的配置
app.config['MAIL_SERVER'] = 'smtp.sina.com' #Flask-Mail需要的配置:邮件服务器地址
app.config['MAIL_PORT'] = 587 #Flask-Mail需要的配置:网络端口
app.config['MAIL_USE_TLS'] = True #Flask-Mail需要的配置:是否开启TLS
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') #Flask-Mail需要的配置:邮箱用户名(值从系统环境获取)
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD') #Flask-Mail需要的配置:邮箱密码(值从系统环境获取)
app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]' #Flask-Mail需要的配置:邮件主题
app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <[email protected]>' #Flask-Mail需要的配置:发件人
app.config['FLASKY_ADMIN'] = os.environ.get('FLASKY_ADMIN') #Flask-Mial需要的配置:收件人地址
bootstrap = Bootstrap(app)
moment = Moment(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
mail = Mail(app)
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User', backref='role', lazy='dynamic')
def __repr__(self):
return '<Role %r>' % self.name
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __repr__(self):
return '<User %r>' % self.username
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
mail.send(msg)
class NameForm(FlaskForm):
name = StringField('What is your name?', validators=[DataRequired()])
submit = SubmitField('Submit')
@app.shell_context_processor
def make_shell_context():
return dict(db=db, User=User, Role=Role)
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username=form.name.data)
db.session.add(user)
db.session.commit()
session['known'] = False
if app.config['FLASKY_ADMIN']:
send_email(app.config['FLASKY_ADMIN'], 'New User',
'mail/new_user', user=user)
else:
session['known'] = True
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'),
known=session.get('known', False))
print('hello')
if __name__=='__main__':
app.run()
来看:
# hello.py
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
mail.send(msg)
构造好Message的实例msg,再设置body和html属性,最后调用Mail的实例的send()就可以直接发送 。
Mail对应于邮箱:服务器地址、用户名、密码、网络端口、是否使用TLS或SSL等参数,都在mail =Mail(app)构造时设置了。
Message对应于邮件:邮件主题、发送方(name <[email protected]>的固定格式)、收件人地址、邮件纯文本、邮件富文本需要要实例msg设置。
最后调用mail.send()方法发送。
三、异步发送邮件
1、多线程发送邮件
def send_async_email(app,msg):
with app.app_context(): #调用app.app_context()方法,激活程序上下文
mail.send(msg) #mail.send()需要激活程序上下文current_app(在不同线程中,程序上下文要使用app.current_app()人工创建)
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
thr = Thread(target=send_async_email,args=[app,msg])
thr.start() #让另一个线程去发送邮件,程序接着往下走
return thr
mail.send()需要激活程序上下文current_app(在不同线程中,程序上下文要使用app.current_app()人工创建)
这样改完之后,客户端不需要等待,可以直接看到页面。因为,最耗时间的发送邮件步骤由后台的其他线程去执行了。
2、多进程发送邮件
本想试试:使用多进程实现异步发送邮件
# hello.py
from multiprocessing import Process
def send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
thr = Process(target=send_async_email,args=[app,msg])
thr.start() #让另一个线程去发送邮件,程序接着往下走
return thr
出现了EOFError:文件尾错误
3、协程异步发送邮件
# hello.py
import asyncio
@asyncio.coroutine
def use_asyncio_send_email(to, subject, template, **kwargs):
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs)
a = yield from mail.send(msg)
return a
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username=form.name.data)
db.session.add(user)
db.session.commit()
session['known'] = False
if app.config['FLASKY_ADMIN']:
# send_email(app.config['FLASKY_ADMIN'], 'New User',
# 'mail/new_user', user=user)
loop = asyncio.get_event_loop()
loop.run_until_complete(use_asyncio_send_email(app.config['FLASKY_ADMIN'], 'New User','mail/new_user', user=user))
loop.close()
else:
session['known'] = True
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'),
known=session.get('known', False))
错误分析:mail.send(msg) 没有返回值(即None),yield from 后面必须要跟一个generator(Iterable 可迭代的)。因此,出现此错误 。