1. 程式人生 > >Flask專案:使用Flask-Mail + uwsgi的郵件傳送

Flask專案:使用Flask-Mail + uwsgi的郵件傳送

自己寫了一個個人主頁,準備部署到阿里雲上,環境是

Flask + Nginx + MySQL + uwsgi + CentOS6.8

有一個(也是唯一一個^_^)需要後臺處理的業務就是郵件傳送,使用Flask-Mail來完成。

Flask-Mail 擴充套件提供了一個簡單的介面,可以在 Flask 應用中設定 SMTP, 使得可以在檢視以及指令碼中傳送郵件資訊。

開啟163郵箱的SMTP服務

我使用的是163郵箱的SMTP服務,首先需要先開啟該服務並獲得授權碼。在163郵箱的“設定-POP3/SMTP/IMAP”中可以開啟服務:
這裡寫圖片描述
在“設定-客戶端授權密碼”中可以獲得授權碼,具體可能需要用到手機驗證:
這裡寫圖片描述

安裝Flask-Mail

pip install Flask-Mail

配置Flask-Mail

配置項 預設值 功能
MAIL_SERVER localhost   郵箱伺服器
MAIL_PORT   25  埠
MAIL_USE_TLS    False   是否使用TLS
MAIL_USE_SSL    False   是否使用SSL
MAIL_DEBUG  app.debug   是否為DEBUG模式,列印除錯訊息
MAIL_SUPPRESS_SEND  app.testing 設定是否真的傳送郵件,True不傳送
MAIL_USERNAME   None    使用者名稱,填郵箱
MAIL_PASSWORD   None
密碼,填授權碼 MAIL_DEFAULT_SENDER None 預設傳送者,填郵箱 MAIL_MAX_EMAILS None 一次連線中的傳送郵件的上限 MAIL_ASCII_ATTACHMENTS False 如果 MAIL_ASCII_ATTACHMENTS 設定成 True 的話,檔名將會轉換成 ASCII 的。一般用於新增附件。

郵件是通過一個Mail例項進行管理的:

from flask import Flask
from flask_mail import Mail, Message

app = Flask(__name__)
...
app.config['MAIL_SERVER'
] = os.environ.get('MAIL_SERVER') or '你的郵箱伺服器' app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') or '你的郵箱' app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD') or '你的郵箱授權碼' app.config['MAIL_PORT'] = 465 app.config['MAIL_USE_SSL'] = True app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:[email protected]/flask' ... # 建立例項 mail = Mail(app)

傳送郵件

首先要建立傳送郵件的內容Message例項:

from flask-mail import Message
msg = Message(subject=subject, recipients=[to], sender=app.config['MAIL_USERNAME'])

其中,subject為主題,recipients為接收方,可以設定一個或多個收件人,也可以後續新增,sender為傳送方,如果設定了”MAIL_DEFAULT_SENDER”,就不必再填寫發件人,因為預設情況下會使用配置項的傳送人。
郵件內容可以包括主體以及/或者HTML:

msg.html = 'Email: ' + message[0] + '<br>Message: ' + message[1]
    msg.body = 'Email: ' + message[0] + 'Message: ' + message[1]

最後,傳送郵件的時候請使用Flask應用設定的Mail例項:

mail.send(msg)

完整例子

from flask import Flask, render_template, request, flash, redirect, url_for, current_app
from flask_script import Manager
from flask_sqlalchemy import SQLAlchemy
from flask_mail import Mail, Message
from threading import Thread
import os
from flask_migrate import Migrate, MigrateCommand
from datetime import datetime

base_dir = os.path.abspath(os.path.dirname(__file__))

app = Flask(__name__)

app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY') or 'idandan'
app.config['MAIL_SERVER'] = os.environ.get('MAIL_SERVER') or '你的郵箱伺服器'
app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME') or '你的郵箱'
app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD') or '你的郵箱授權碼'
app.config['MAIL_PORT'] = 465  # 設定郵箱埠為465,預設為25,由於阿里雲禁止了25埠,所以需要修改
app.config['MAIL_USE_SSL'] = True  # 163郵箱需要開啟SSL
#database_uri = 'sqlite:///' + os.path.join(base_dir, 'data.sqlite')
#app.config['SQLALCHEMY_DATABASE_URI'] = database_uri
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:[email protected]/你的資料庫名'

# 建立例項
mail = Mail(app)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)


# 資料庫模型類
class MessageModel(db.Model):
    __tablename__ = 'message'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(128))
    email = db.Column(db.String(64))
    message = db.Column(db.Text)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow())


# 傳送郵件
def send_mail(to, subject, message, **kwargs):
    msg = Message(subject=subject, recipients=[to], sender=app.config['MAIL_USERNAME'])
    msg.html = 'Email: ' + message[0] + '<br>Message: ' + message[1]
    msg.body = 'Email: ' + message[0] + 'Message: ' + message[1]
    mail.send(msg)
    return 'ok'

# 檢視處理路由
@app.route('/send/', methods=['GET', 'POST'])
def send():
    if request.method == 'POST':
        # 接收form表單中傳遞過來的資料
        name = request.form.get('name')
        email = request.form.get('email')
        message = request.form.get('message')

        # 寫入資料庫
        record = MessageModel(name=name, email=email, message=message)
        db.session.add(record)
        db.session.commit()

        # 傳送郵件
        send_mail('收件人郵箱', name, [email, message])
        flash('Email send successfully')
        return redirect(url_for('index'))
    return render_template('index.html')


@app.route('/')
def index():
    return render_template('index.html')


if __name__ == '__main__':
    manager.run()

開始使用了非同步傳送,但是由於使用的是uwsgi,似乎由於GIL的原因,禁止了uwsgi的多執行緒(不確定是不是),所以導致郵件一直髮送不出去,測試了很久,將非同步傳送去掉了以後就成功了,同學們也可以測試一下。另外,在初始化MySQL時,報了”ModuleNotFoundError: No module named ‘MySQLdb’
“的錯誤,這個只要在配置”SQLALCHEMY_DATABASE_URI”的時候,加上一個”pymysql”就可以了,詳細可以見No module named ‘MySQLdb’

分割線
終於找到不能非同步傳送郵件的原因了,uwsgi預設時單執行緒的,每次都是結束後才能給伺服器傳送成功,在後臺啟動檔案”uwsgi.ini”中新增threads選項,指定執行緒數就可以了