Flask 插件系列 - Flask-Mail
簡介
給用戶發送郵件是 Web 應用中最常見的任務之一,比如用戶注冊,找回密碼等。Python 內置了一個 smtplib 的模塊,可以用來發送郵件,這里我們使用 Flask-Mail ,是因為它可以和 Flask 集成,讓我們更方便地實現此功能。
安裝
使用 pip 安裝:
$ pip install Flask-Mail
或下載源碼安裝:
$ git clone https://github.com/mattupstate/flask-mail.git
$ cd flask-mail
$ python setup.py install
發送郵件
Flask-Mail 連接到簡單郵件傳輸協議 (Simple Mail Transfer Protocol, SMTP) 服務器,并把郵件交給這個服務器發送。這里以 QQ 郵箱為例,介紹如何簡單地發送郵件。在此之前,我們需要知道 QQ 郵箱的服務器地址和端口是什么。
# -- coding: utf-8 --
from flask import Flask
from flask_mail import Mail, Message
import os
app = Flask(name)
app.config['MAIL_SERVER'] = 'smtp.qq.com' # 郵件服務器地址
app.config['MAIL_PORT'] = 25 # 郵件服務器端口
app.config['MAIL_USE_TLS'] = True # 啟用 TLS
app.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('/')
def index():
msg = Message('Hi', sender='me@example.com', recipients=['he@example.com'])
msg.html = '<b>Hello Web</b>'
# msg.body = 'The first email!'
mail.send(msg)
return '<h1>OK!</h1>'
if name == 'main':
app.run(host='127.0.0.1', debug=True)</code></pre>
在發送前,需要先設置用戶名和密碼,當然你也可以直接寫在文件里,如果是從環境變量讀取,可以這么做:
$ export MAIL_USERNAME='me@example.com'
$ export MAIL_PASSWORD='123456'
將上面的 sender 和 recipients 改一下,就可以進行測試了。
從上面的代碼,我們可以知道,使用 Flask-Mail 發送郵件主要有以下幾個步驟:
-
配置 app 對象的郵件服務器地址,端口,用戶名和密碼等
-
創建一個 Mail 的實例: mail = Mail(app)
-
創建一個 Message 消息實例,有三個參數:郵件標題、發送者和接收者
-
創建郵件內容,如果是 HTML 格式,則使用 msg.html ,如果是純文本格式,則使用 msg.body
-
最后調用 mail.send(msg) 發送消息
Flask-Mail 配置項
Flask-Mail 使用標準的 Flask 配置 API 進行配置,下面是一些常用的配置項:
配置項
說明
MAIL_SERVER
郵件服務器地址,默認為 localhost
MAIL_PORT
郵件服務器端口,默認為 25
MAIL_USE_TLS
是否啟用傳輸層安全 (Transport Layer Security, TLS)協議,默認為 False
MAIL_USE_SSL
是否啟用安全套接層 (Secure Sockets Layer, SSL)協議,默認為 False
MAIL_DEBUG
是否開啟 DEBUG,默認為 app.debug
MAIL_USERNAME
郵件服務器用戶名,默認為 None
MAIL_PASSWORD
郵件服務器密碼,默認為 None
MAIL_DEFAULT_SENDER
郵件發件人,默認為 None,也可在 Message 對象里指定
MAIL_MAX_EMAILS
郵件批量發送個數上限,默認為 None
MAIL_SUPPRESS_SEND
默認為 app.testing,如果為 True,則不會真的發送郵件,供測試用
異步發送郵件
使用上面的方式發送郵件,會發現頁面卡頓了幾秒才出現消息,這是因為我們使用了同步的方式。為了避免發送郵件過程中出現的延遲,我們把發送郵件的任務移到后臺線程中,代碼如下:
# -- coding: utf-8 --
from flask import Flask
from flask_mail import Mail, Message
from threading import Thread
import os
app = Flask(name)
app.config['MAIL_SERVER'] = 'smtp.qq.com'
app.config['MAIL_PORT'] = 25
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') or 'smtp.example.com'
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD') or '123456'
mail = Mail(app)
def send_async_email(app, msg):
with app.app_context():
mail.send(msg)
@app.route('/sync')
def send_email():
msg = Message('Hi', sender='me@example.com', recipients=['he@example.com'])
msg.html = '<b>send email asynchronously</b>'
thr = Thread(target=send_async_email, args=[app, msg])
thr.start()
return 'send successfully'
if name == 'main':
app.run(host='127.0.0.1', debug=True)</code></pre>
在上面,我們創建了一個線程,執行的任務是 send_async_email ,該任務的實現涉及一個問題 1 :
很多 Flask 擴展都假設已經存在激活的程序上下文和請求上下文。Flask-Mail 中的 send() 函數使用 current_app ,因此必須激活程序上下文。不過,在不同線程中執行 mail.send() 函數時,程序上下文要使用 app.app_context() 人工創建。
帶附件的郵件
有時候,我們發郵件的時候需要添加附件,比如文檔和圖片等,這也很簡單,代碼如下:
# -- coding: utf-8 --
from flask import Flask
from flask_mail import Mail, Message
import os
app = Flask(name)
app.config['MAIL_SERVER'] = 'smtp.qq.com' # 郵件服務器地址
app.config['MAIL_PORT'] = 25 # 郵件服務器端口
app.config['MAIL_USE_TLS'] = True # 啟用 TLS
app.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')
def add_attchments():
msg = Message('Hi', sender='me@example.com', recipients=['other@example.com'])
msg.html = '<b>Hello Web</b>'
with app.open_resource("/Users/Admin/Documents/pixel-example.jpg") as fp:
msg.attach("photo.jpg", "image/jpeg", fp.read())
mail.send(msg)
return '<h1>OK!</h1>'
if name == 'main':
app.run(host='127.0.0.1', debug=True)</code></pre>
上面的代碼中,我們通過 app.open_resource(path_of_attachment) 打開了本機的某張圖片,然后通過 msg.attach() 方法將附件內容添加到 Message 對象。 msg.attach() 方法的第一個參數是附件的文件名,第二個參數是文件內容的 MIME (Multipurpose Internet Mail Extensions) 類型,第三個參數是文件內容。
批量發送
在某些情況下,我們需要批量發送郵件,比如給網站的所有注冊用戶發送改密碼的郵件,這時為了避免每次發郵件時都要創建和關閉跟服務器的連接,我們的代碼需要做一些調整,類似如下:
with mail.connect() as conn:
for user in users:
subject = "hello, %s" % user.name
msg = Message(recipients=[user.email], body='...', subject=subject)
conn.send(msg)
上面的工作方式,使得應用與電子郵件服務器保持連接,一直到所有郵件已經發送完畢。某些郵件服務器會限制一次連接中的發送郵件的上限,這樣的話,你可以配置 MAIL_MAX_EMAILS 。
需要注意的是,更好的發送大量電子郵件的方式是用專門的作業系統,比如用 Celery 任務隊列等。
來自:https://segmentfault.com/a/1190000007378073