Flask框架——Flask-WTF表單:文件上傳、驗(yàn)證碼

在上篇文章中,我們學(xué)習(xí)了Flask框架——Flask-WTF表單:數(shù)據(jù)驗(yàn)證、CSRF保護(hù),這篇文章我們學(xué)習(xí)Flask框架——Flask-WTF表單:文件上傳、驗(yàn)證碼。

文件上傳

Flask-WTF表單提供FileField字段來(lái)處理文件上傳,它在表單提交后,自動(dòng)從flask.request.files中抽取數(shù)據(jù)。

示例代碼如下所示:

import os
from flask import Flask, render_template
from flask_wtf import FlaskForm, CSRFProtect
from flask_wtf.file import FileField, FileRequired, FileAllowed

app = Flask(__name__)
app.config['SECRET_KEY']='hakhfaskh'      #配置CSRF需要的密鑰,其值是任意的       
csrf = CSRFProtect(app)          #將CSRF保護(hù)加入到app中

class Myform(FlaskForm):
    file = FileField(label='用戶頭像上傳',validators=[FileRequired(), FileAllowed(['jpg','png'])])   #創(chuàng)建FileField字段

@app.route('/',methods=['GET','POST'])
def hello_world():
    myform = Myform()         #創(chuàng)建表單對(duì)象
    if myform.validate_on_submit():          #檢查是否是一個(gè)POST請(qǐng)求并且請(qǐng)求是否有效
        filename=myform.file.data.filename    #獲取傳入的文件名
        filepath=os.path.dirname(os.path.abspath(__file__))       #獲取當(dāng)前項(xiàng)目的文件路徑
        savepath=os.path.join(filepath,'static')      #設(shè)置保存文件路徑        
        myform.file.data.save(os.path.join(savepath,filename))    #保存文件
        return '提交成功'
    return render_template('file.html', myform=myform)  #使用render_template()方法渲染file.html文件并將myform傳遞到file.html中

if __name__ == '__main__':
    app.run(debug=True)

配置CSRF需要的密鑰并將CSRF保護(hù)加入到app中,然后創(chuàng)建名為Myform的表單類,在類中我們定義了FileField文件字段并使用了FileRequired文件驗(yàn)證函數(shù)和FileAllowed文件類型驗(yàn)證函數(shù)。

在視圖函數(shù)中創(chuàng)建表單類對(duì)象,使用了validate_on_submit()方法檢查是否是一個(gè)POST請(qǐng)求并且請(qǐng)求是否有效,獲取傳入的文件名并定義保存文件的路徑,最后使用render_template()方法渲染file.html文件并將myform傳遞到file.html中。

創(chuàng)建名為file.html文件,其內(nèi)容如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="{{ url_for('hello_world') }}" method="post" enctype="multipart/form-data">
        {{ myform.csrf_token }}     {#渲染csrf#}
        <p>{{ myform.file.label }}{{ myform.file }}</p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

注意:涉及到文件相關(guān)的,需要在 HTML 表單的 enctype 設(shè)置成 multipart/form-data。

啟動(dòng)flask項(xiàng)目,訪問(wèn)http://127.0.0.1:5000/并選擇文件,如下圖所示:

驗(yàn)證碼

Flask-WTF 通過(guò) RecaptchaField 也提供對(duì)驗(yàn)證碼的支持,示例代碼如下:

from flask import Flask, render_template
from flask_wtf import FlaskForm, RecaptchaField, CSRFProtect

app = Flask(__name__)
app.config['SECRET_KEY']='hakhfaskh'    #配置CSRF需要的密鑰,其值是任意的
csrf = CSRFProtect(app)              #將CSRF保護(hù)加入到app中

class Myform(FlaskForm):
    recaptcha = RecaptchaField()    #驗(yàn)證碼字段

@app.route('/')
def yanzm():
    myform=Myform()         #創(chuàng)建表單對(duì)象
    return render_template('yanzm.html', myform=myform)     #使用render_template()方法渲染yanzm.html文件并將myform傳遞到file.html中

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

配置CSRF需要的密鑰并將CSRF保護(hù)加入到app中,創(chuàng)建名為Myform的表單類,在類中我們定義了RecaptchaField驗(yàn)證碼字段,在視圖函數(shù)中我們創(chuàng)建表單對(duì)象,使用render_template()方法渲染yanzm.html文件并將myform傳遞到file.html中。

使用驗(yàn)證碼還需要配置如下參數(shù):

  • RECAPTCHA_PUBLIC_KEY:必選,公鑰;

  • RECAPTCHA_PRIVATE_KEY:必選,私鑰;

  • RECAPTCHA_API_SERVER:可選,驗(yàn)證碼API服務(wù)器;

  • RECAPTCHA_PARAMETERS:可選,一個(gè)JavaScript(api.js)參數(shù)的字典;

  • RECAPTCHA_DATA_ATTRS:可選,一個(gè)數(shù)據(jù)屬性項(xiàng)列表 https://developers.google.com/recaptcha/docs/display。

創(chuàng)建名為yanzm.html,其內(nèi)容如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
   <form action="/" method="post">
        {{ myform.csrf_token }}
       {{ myform.recaptcha }}
   </form>
</body>
</html>

啟動(dòng)flask項(xiàng)目,訪問(wèn)http://127.0.0.1:5000/,發(fā)現(xiàn)什么都沒(méi)顯示,這是因?yàn)镽ecaptchaField()字段需要調(diào)用谷歌的驗(yàn)證碼API,而調(diào)用谷歌的驗(yàn)證碼API需要翻墻,我們沒(méi)有翻墻,所以什么都沒(méi)顯示,如下圖所示:


使用RecaptchaField()驗(yàn)證碼字段需要翻墻,那么我們就不使用它,我們自己繪制驗(yàn)證碼。

這里我們繪制驗(yàn)證碼使用pillow圖形庫(kù),其執(zhí)行如下代碼安裝:

pip install pillow

安裝好pillow后,我們創(chuàng)建一個(gè)名為uitl.py文件,其作用是繪制我們的圖形驗(yàn)證碼,代碼如下:

import random
from PIL import Image, ImageFont, ImageDraw

#自定義get_color方法,獲取三位隨機(jī)數(shù)并保存在元組中
def get_color():
    return (random.randint(0,255),random.randint(0,255),random.randint(0,255))

#自定義generate_image方法繪制圖片驗(yàn)證碼
def generate_image(4):
    size=(130,50)                           #設(shè)置驗(yàn)證碼的大小
    im=Image.new('RGB',size,color=get_random_color())   #創(chuàng)建驗(yàn)證碼背景圖
    font=ImageFont.truetype('C:\Windows\Fonts/simsun.ttc',size=40)  #設(shè)置驗(yàn)證碼字體
    draw=ImageDraw.Draw(im)    #創(chuàng)建ImageDraw對(duì)象
    code=''             #設(shè)置驗(yàn)證碼值
    s='abcdefghijklmnopqrstuvwxyz123456'        #設(shè)置驗(yàn)證碼的取值范圍
    for i in range(4):          #繪制4位數(shù)驗(yàn)證碼值
        c=random.choice(s)          #隨機(jī)在s中取值
        code+=c                     #將取到的值放在驗(yàn)證碼中
        draw.text((9+random.randint(4,7)+20*i,random.randint(4,7)),text=c,fill=get_random_color(),font=font)    #在驗(yàn)證碼背景圖中寫(xiě)入驗(yàn)證碼值
    im.show()                   
    return im ,code             #返回圖片和驗(yàn)證碼值

我們自定義get_color方法獲取三位隨機(jī)數(shù)保存在元組并返回,再自定義generate_image()方法繪制圖片驗(yàn)證碼,設(shè)置驗(yàn)證碼的取值范圍、背景圖大小字體等,并隨機(jī)獲取驗(yàn)證碼值寫(xiě)入驗(yàn)證碼背景圖中。

大家可以根據(jù)pillow庫(kù)的文檔來(lái)為驗(yàn)證碼增加一些干擾線,這樣簡(jiǎn)易版的圖形驗(yàn)證碼就制作完畢了,如下圖所示:



這里我們是繪制了4個(gè)字符的圖片驗(yàn)證碼,圖片驗(yàn)證碼代碼生成已經(jīng)寫(xiě)好了,接下來(lái)我們編寫(xiě)flask項(xiàng)目來(lái)使用驗(yàn)證碼,示例代碼如下所示:

from flask import Flask, render_template, session
from flask_wtf import FlaskForm, CSRFProtect
from wtforms import StringField, ValidationError

app = Flask(__name__)
app.config['SECRET_KEY']='hakhfaskh'      #配置CSRF需要的密鑰,其值是任意的       
csrf = CSRFProtect(app)          #將CSRF保護(hù)加入到app中

class Myform(FlaskForm):            #創(chuàng)建表單類
    recaptcha = StringField()           #創(chuàng)建文本字段
    def validate_recaptcha(self, data):         #自定義驗(yàn)證
        input_code=data.data                   #輸入的驗(yàn)證碼的值
        code=session.get('valid')               #獲取session的valid值
        if input_code.lower()!=code.lower():        #驗(yàn)證碼值轉(zhuǎn)換為小寫(xiě)并判斷輸入的驗(yàn)證碼和圖片驗(yàn)證碼值是否一致
            raise ValidationError('驗(yàn)證碼錯(cuò)誤')      #拋出錯(cuò)誤

@app.route('/',methods=['GET','POST'])
def yanzm():
    myform=Myform()                             #創(chuàng)建表單類對(duì)象
    if myform.validate_on_submit():             #檢查是否是一個(gè)POST請(qǐng)求并且請(qǐng)求是否有效
        return '驗(yàn)證成功'
    return render_template('yanzm.html', myform=myform)     #使用render_template()方法渲染yanzm.html文件并將myform傳遞到file.html中

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

設(shè)置CSRF保護(hù),創(chuàng)建表單類使用StringField()文本字段并自定義驗(yàn)證函數(shù),然后在視圖函數(shù)中創(chuàng)建表單類對(duì)象,使用validate_on_submit()方法檢查是否是一個(gè)POST請(qǐng)求并且請(qǐng)求是否有效,使用render_template()方法渲染yanzm.html文件并將myform傳遞到file.html中。

是不是這樣就可以了呢?這里我們還需要?jiǎng)?chuàng)建一個(gè)視圖函數(shù)來(lái)獲取圖片驗(yàn)證碼,代碼如下所示:

from util import generate_image         #導(dǎo)入剛才編寫(xiě)的繪制驗(yàn)證碼中的generate_image
@app.route('/image')
def get_image():
    im,code=generate_image()    #獲取驗(yàn)證碼圖片與值
    buffer=BytesIO()            #將image對(duì)象轉(zhuǎn)成二進(jìn)制
    im.save(buffer,"JPEG")      #以二進(jìn)制形式保存為JPEG格式
    buf_bytes=buffer.getvalue()     #讀取二進(jìn)制驗(yàn)證碼
    session['valid']=code       #保存到session中,控制驗(yàn)證碼的失效時(shí)間
    response=make_response(buf_bytes)       #構(gòu)造response對(duì)象
    response.headers['Content-Type']='image/jpg'    #定制請(qǐng)求頭
    return response

在視圖函數(shù)中,我們通過(guò)調(diào)用generate_image()方法來(lái)獲取驗(yàn)證碼圖片與值并將圖片以二進(jìn)制形式保存為JPEG格式,由于一般情況下驗(yàn)證碼是有失效時(shí)間的,所以我們使用session控制驗(yàn)證碼的實(shí)現(xiàn)時(shí)間,使用make_response()構(gòu)造response對(duì)象為驗(yàn)證碼圖片定制請(qǐng)求頭。

接下來(lái)我們編寫(xiě)yanzm.html文件,代碼如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
   <form action="/" method="post">
        {{ myform.csrf_token }}     {#渲染csrf#}
       {{ myform.recaptcha }}<img src="{{ url_for('get_image') }}" alt="" id="img">
        <p>{{ myform.recaptcha.errors.0 }}</p>
        <input type="submit" value="提交">
   </form>
</body>
</html>

一般情況下,只要我們點(diǎn)擊圖片驗(yàn)證碼就會(huì)刷新,所以我們可以加入以下代碼來(lái)控制圖片驗(yàn)證碼的刷新:

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
    $('#img').click(function (){
        $(this).attr('src',"{{ url_for('get_image') }}?ran="+Math.random());
    })
</script>

這里我們調(diào)用了百度的jquery并為驗(yàn)證碼添加了點(diǎn)擊事件。

好了,這樣就成功繪制了我們的圖片驗(yàn)證碼了,啟動(dòng)flask項(xiàng)目,訪問(wèn)http://127.0.0.1:5000/,如下圖所示:


當(dāng)我們正確輸入時(shí)并點(diǎn)擊提交,就會(huì)顯示驗(yàn)證成功,當(dāng)我們錯(cuò)誤輸入并點(diǎn)擊提交,就會(huì)顯示驗(yàn)證碼錯(cuò)誤。

好了,F(xiàn)lask框架——Flask-WTF表單:文件上傳、驗(yàn)證碼就講到這里了,感謝觀看,下篇文章學(xué)習(xí)Flask框架——Flask-Mail郵件。

公眾號(hào):白巧克力LIN

該公眾號(hào)發(fā)布Python、數(shù)據(jù)庫(kù)、Linux、Flask、自動(dòng)化測(cè)試、Git等相關(guān)文章!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容