在上篇文章中,我們學(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)文章!