使用Flask-Mail发送电子邮件

Flask-Mail包装了python标准库中的stmplib包并简化了在flask中发送电子邮件的过程

初始化

安装: pip install flask-mail
初始化:使用flask-mail提供的Mail类并传入实例完成初始化

  1. from flask-mail import Mail
  2. app = Flask(__name__)
  3. mail = Mail(app)

配置Flask-Mail

Flask-mail通过连接SMTP(Simple Mail Transfer Protocol简单邮件传输协议)服务器来发送邮件,因此在发送邮件之前需要配置STMP服务器。可以电脑本地安装stmp服务器, 开发测试阶段可以使用邮件服务提供商的SMTP服务器如gmail、163。

Flask-Mail常用配置变量

配置键 说明 默认值
MAIL_SERVER 用于发送邮件的STMP服务器 localhost
MAIL_PORT 发送端口 25
MAIL_USE_TLS 是否使用STARTLS False
MAIL_USE_SSL 是否使用SSL/TLS False
MAIL_USERNAME 发送服务器的用户名 None
MAIL_PASSWORD 发送服务器的密码 None
MAIL_DEFAULT_SENDER 默认的发信人,由一个两元素元组组成(姓名,邮箱地址) 。 Flask_Mail会把这个元组转换成标准发信人格式即:姓名 <邮箱地址>。也可以直接使用这个格式指定发信人. None
  • 对发送的邮件进行加密,可以避免邮件在发送的过程被第三方截获篡改,SSL和TLS是常用的电子邮件安全协议,TSL继承了SSL并在SSL的基础上做了一些改进。因此多数情况下SSL和TSL可以互相转换,通过MAIL_USE_SSL设置为True启用
  • STASRTTLS是另外一种加密方式,对不安全的连接进行升级。

根据加密方式不同端口也要相应的改变

  1. #SSL/TSL 加密
  2. MAIL_USE_SSL=True
  3. MAIL_PORT=465
  4. # STARTTSL加密
  5. STARTTSL=True
  6. MAIL_PORT = 587
  7. #不对邮件进行加密时端口使用默认的25

常用的邮件服务商STMP配置信息:

服务商 MAIL_SERVER MAIL_USERNAME MIAL_PASSWORD 其他
GMAIL smtp.gmail.com 邮箱账号 邮箱密码 开启allow less secure apps,在本地设置vpn代理
QQ邮箱 smtp.qq.com 邮箱账号 授权码 开启STMP服务并获得授权码
163邮箱 smtp.163.com 邮箱账号 授权码 开启STMP服务并获得授权码
  • 163邮箱的SMTP不支持START_TSL,需要使用TSL/SSL。 具体设置为MAIL_USE_SSL=True,MAIL_PORT=465。
  • 发送大量邮件更好的选择是使用自己配置的STMP服务器或使用类似SendGrid、Mailgun的事务性服务提供商(Transactional Email Service)。

  • 大量的配置可以使用app.config()对象的update()方法加载配置。在实例化Mail类时,Flask-Mail会获取配置创建一个用于发信的对象, 因此确保在实例化之前加载配置。 ```python from flask_mail import Mail

app = Flask(name)

app.config.update( MAIL_SERVER=os.getenv(‘MAIL_SERVER’), MAIL_PORT=587, MAIL_USE_TSL=True, MAIL_USERNSAME=os.getenv(‘MAIL_USERNAME’), MAIL_PASSWORD=os.getenv(‘MAIL_PASSWORD’), MAIL_DEFAULT_SENDER=(‘Mike’, os.getenv(‘MAIL_USERNAME’)) #或MAIL_DEFAULT_SENDER=’姓名 <邮箱地址>’。 设置后在发信人可以不用再设置。 )

mail = Mail(app)

铭感信息从环境变量获取, 可以使用set/export设置环境变量,不过建议把这些保存在.env。

  1. <a name="wd4tG"></a>
  2. ### 构建邮件数据并发送
  3. - 邮件通过Flask-Mail的Message类表示。
  4. - 一封邮件至少包括主体、收件人、正文、发件人。分别通过Message类中的subject、recipients、body关键字传入参数。其中recipients时一个包含邮件地址的列表
  5. - 发送邮件功能通过实例化的mail对象调用send()方法实现,传入构造的邮件对象。
  6. ```python
  7. from flask-mail import Message
  8. from app import mail
  9. message = Message(subject='Hello Email', recipients=['hehe' <hehe.reedo.cn>], body='hello ,test content')
  10. mail.send(message)

抽象成方法调用

  1. #定义send_mail()方法
  2. from flask_mail import Message
  3. from app import mail
  4. def send_mail(subject, to, body):
  5. message=Message(subject=subject, recipients=[to], body = body)
  6. mail.send(message)
  7. #使用,调用send_mail函数即可

使用事物邮件服务SendGrid

在生产环境除了自己安装运行邮件服务器外,更便捷的方法是使用事物邮件服务如Mailgun、sendgrid。

注册sengrid

  • 注册:https://sendgrid.com/
  • 注册完成后为项目创建一个API密钥。 作为环境变量SENDGRID_API_KEY=’key’。

SendGrid STMP转发

设置STMP服务器

  1. from flask_mail import Mail
  2. app = Flask(__name__)
  3. app.config.update(
  4. MAIL_SERVER=os.getenv('MAIL_SERVER'),
  5. MAIL_PORT=587,
  6. MAIL_USE_TSL=True,
  7. MAIL_USERNSAME=os.getenv('MAIL_USERNAME'),
  8. MAIL_PASSWORD=os.getenv('SENDGRID_API_KEY'),
  9. MAIL_DEFAULT_SENDER=('Mike', os.getenv('MAIL_USERNAME')) #或MAIL_DEFAULT_SENDER='姓名 <邮箱地址>'。 设置后在发信人可以不用再设置。
  10. )
  11. mail = Mail(app)
  12. # 铭感信息从环境变量获取, 可以使用set/export设置环境变量,不过建议把这些保存在.env。
  13. MAIL_SERVER='stmp.sendgrid.net'
  14. MAIL_USERNAME='api key'

SendGrid Web API转发

SendGrid还支持通过它提供的Web API转发邮件, 好处是更安全、速度更快。

  • 在程序中向SendGrid提供的WebAPI发出一个POST请求并且附带必要的信息如密钥、邮件主题、收件人、正文等,SendGrid就会为发我们发送邮件。
    • 接口URL:
    • Authorization首部字段提供相应的API密钥
    • 报文主体:JSON格式表示的电子邮件数据。

手动编写发送邮件内容

使用SendGrid提供的SDK(Software Development Kit软件开发工具包)。
安装: pip install sendgrid
构建邮件并发送:

  1. # 1 创建发信对象
  2. from sendgrid import SendGridApiClient
  3. sg = SendGridApiClient(apikey=os.getenv('SENDGRID_API_KEY'))
  4. # 2 构建邮件内容
  5. from sendgrid.helpers.mail import Email, Content, Mail
  6. from = Email('from@reedo.cn' 'Reedo官网') # Email用来创建邮件地址,依次传入邮箱地址和姓名, 3中形式均可
  7. to = Email('user@reedo.cn')
  8. subject = 'hello Email'
  9. content = Content('text/plain', 'email content') #接收MIME类型(type_)和正文value
  10. mail = Mail(from, subject, to , content) #构造邮件对象,分别传入发信人、主题、收信人、正文
  11. #通过mail.get()可以获得邮件内容
  12. # 对sg对象调用sg.client.mail.send.post()方法并用关键字传入邮箱对象即可发送发信的POST请求
  13. sg.client.mail.send.post(request_body=mail.get)

抽象成方法调用

  1. #定义send_mail()方法
  2. from sendgrid import SendGridApiClient
  3. from sendgrid.helpers.mail import Email, Content, Mail
  4. def send_mail( subject, to, body):
  5. from_email = Email('from@email.com', 'Reedo官网')
  6. to = Email(to)
  7. content = Content('text/plain', body)
  8. mail = Mail(from_email, subject, to, content)
  9. sg = SendGridApiClinet(apikey=os.getev('SENDGRID_API_KEY'))
  10. response = sg.mail.send.post(request_body=mail.get())
  11. #使用,调用send_mail函数即可

电子邮件进阶

提供HTML正文

  • 一般在HTML邮件中,html和文本都提供,在无法显示html的客户端中显示文本。
  • 最佳实践:1 table布局而不使用div,2 行内样式css; 3 宽度不超过600px; 4 不适应js、背景图片。

Flask-Mail中:
Message类中HTML正文可以在实例化时传入html参数指定; 或 通过类属性message.html指定

  1. #1
  2. message = Message(... , body='正文..', html='<h1>标题1 </h1>,html内容')
  3. # 2 方式2
  4. message = Message(...)
  5. message.body = '..'
  6. message.html = '..'

SendGrid-Python中:
使用Content类构造邮件正文中传入的第一个type_参数指定类型

  • 如果是html, 则type=’text/html’
  • 如果要邮件同时提供这两种格式的文字,则在Mail类构造邮件对象是传入包含两个content类实例的列表多为content的参数值
  1. text_content = Content('text/plain', '文本正文')
  2. html_content = Content('text/html', <h1>html内容</h1>)
  3. mail = Mail(..., content = [text_content, html_content])

使用Jinja2模板组织邮件正文

在发送邮件的函数中使用render_template()函数渲染邮件正文

  1. from flask import render_template
  2. from flask_mail import Message
  3. def send_welcome_mail(subject, to, body, **kwargs):
  4. message = Message(subject, recipients=[to] )
  5. message.body = render_template('email/welcome.txt', **kwargs)
  6. message.html = render_template('email/welcome.html', **kwargs)
  7. mail.send(message)

如果使用SendGrid的WebApi发送邮件,可以使用SendGrid提供的在线模板功能,快速编写邮件正文并支持用特殊语法标记变量。 在程序中发送邮件时传入变量及对应的模板id。

异步发送邮件

  1. from threading import Thread
  2. def _send_async_mail(app, message):
  3. with app.app_context():
  4. mail.send(message) #因为send()在内部调用了current_app变量, 因此需要激活上下文
  5. def send_mail(subject, to , body):
  6. message = Message(subject, recipients = [to], body=body)
  7. thr = Thread(target=_send_asyc_mail, args=[app, message])
  8. thr.start()
  9. return thr