某天在Stack Overflow上看到一個關於Dropzone.js的問題,研究之後寫了一個回答。我發現,如果有一個整合Dropzone.js到Flask,並且簡化設定步驟的擴充套件,肯定要比其他上傳方式簡單的多——於是就有了Flask-Dropzone。
Dropzone.js是一個提供檔案上傳、驗證、預覽、上傳進度條等功能的JavaScript庫。Flask-Dropzone在模板中提供了一些方法來幫助你建立上傳區域,引入相關資源。你只需要新增一些配置就可以實現上傳型別的過濾,檔案大小限制,上傳後跳轉等功能。當然,你還要自己編寫檢視函式來處理和儲存檔案,並進行伺服器端的二次驗證。如果你不熟悉伺服器端的上傳檔案處理,可以考慮瀏覽一下這篇《Flask檔案上傳(一):原生實現》。
《Flask Web開發實戰》中的第3個示例程式(圖片社交程式Albumy)使用了這個擴充套件。
用法介紹
安裝
$ pip install flask-dropzone複製程式碼
初始化
和其他擴充套件類似,你可以通過例項化Dropzone類,並傳入程式例項app進行初始化:
from flask_dropzone import Dropzone
app = Flask(__name__)
dropzone = Dropzone(app)複製程式碼
在使用工廠函式建立程式例項時,你也可以使用init_app()方法:
from flask_dropzone import Dropzone
dropzone = Dropzone()
def create_app():
app = Flask(__name__)
dropzone.init_app(app)
return app複製程式碼
引入Dropzone.js資源
你需要自己手動編寫引入Dropzone.js的CSS和JavaScript資源的語句。在開發時,或對於玩具專案,你可以可以使用Flask-Dropzone提供的兩個快捷方法:
<head>
...
{{ dropzone.load_css() }}
</head>
<body>
...
{{ dropzone.load_js() }}
</body>複製程式碼
建立並美化上傳區域
如果你不需要對上傳區域的樣式有太多控制,那麼你只需要在想要渲染上傳區域的地方使用dropzone.create()方法:
{{ dropzone.create(action='處理上傳檔案的路由URL') }}複製程式碼
記得把action引數的值更改成你要處理檔案上傳的的URL。你可以使用dropzone.style()方法為上傳區域新增簡單的自定義樣式:
<head>
... <!-- 在引入Dropzone.js的CSS檔案後呼叫style()方法 -->
{{ dropzone.style('border: 2px dashed #0087F7; margin: 10%') }}
</head>複製程式碼
上傳區域的截圖示例如下所示:
在伺服器端處理並儲存上傳檔案
當檔案被拖拽到上傳區域,或是點選上傳區域選擇上傳檔案後,這些檔案會以AJAX請求的形式傳送到你在dropzone.create()方法中使用action引數傳入的URL。我們需要在伺服器端建立對應的檢視函式來處理這些請求,下面是一個最基本的示例:
import os
from flask import Flask, request
from flask_dropzone import Dropzone
app = Flask(__name__)
dropzone = Dropzone(app)
@app.route('/uploads', methods=['GET', 'POST'])
def upload():
if request.method == 'POST': # 如果請求型別為POST,說明是檔案上傳請求
f = request.files.get('file') # 獲取檔案物件
f.save(os.path.join('the/path/to/save', f.filename)) # 儲存檔案
return 'upload template' # 渲染上傳頁面複製程式碼
上傳完成後重定向
這裡需要注意的是,因為Dropzone.js通過AJAX請求提交檔案,所以你沒法在儲存檔案後將頁面重定向。對於這個問題,你可以使用配置變數DROPZONE_REDIRECT_VIEW設定上傳完成後跳轉到的目標端點,或是新增一個按鈕讓使用者自己點選進行跳轉。
伺服器端驗證
儘管Dropzone.js可以在前端對使用者提交的檔案進行驗證,但為了安全考慮,我們仍然需要在伺服器端進行二次驗證。在伺服器端驗證時,如果驗證出錯,我們不能像往常那樣使用flash()函式“閃現”錯誤訊息,因為AJAX請求接受到響應後並不會過載頁面,所以不會顯示通過flash()函式傳送的訊息。正確的做法是返回400錯誤響應,使用錯誤訊息作為響應的主體。下面是一個簡單的進行伺服器端驗證並返回錯誤訊息得示例:
@app.route('/', methods=['POST', 'GET'])
def upload():
if request.method == 'POST':
f = request.files.get('file')
if f.filename.split('.')[1] != 'png':
return 'PNG only!', 400 # return the error message, with a proper 4XX code
f.save(os.path.join('the/path/to/save', f.filename))
return render_template('index.html')複製程式碼
在上面的程式碼中,我們驗證圖片是不是png格式,如果不是就返回一個錯誤提示,在伺服器端會在圖片下面看到我們返回的錯誤訊息:
完整的配置列表
Flask-Dropzone提供了豐富的配置變數,你可以使用它們對Dropzone.js進行各類配置。很遺憾掘金的編輯器不支援表格,所以這裡只能插入一張圖片,如果你需要文字,可以訪問這篇文章。
設定檔案型別過濾
Flask-Dropzone內建了一些檔案型別(通過MIME定義),可選的值和對應的檔案型別如下所示:
- default:預設值,執行所有型別
- image:圖片
- audio:音訊
- video:視訊
- text:文字
- app:程式
你需要為DROPZONE_ALLOWED_FILE_TYPE設定對應的值,比如下面設定僅允許上傳圖片:
app.config['DROPZONE_ALLOWED_FILE_TYPE'] = 'image'複製程式碼
如果你想要自己定義允許的檔案型別列表,那麼你需要將DROPZONE_ALLOWED_FILE_CUSTOM設定True,然後傳入一個包含允許的檔案字尾名列表組成的字串給DROPZONE_ALLOWED_FILE_TYPE變數,使用逗號分隔多個字尾名,比如:
app.config['DROPZONE_ALLOWED_FILE_CUSTOM'] = True
app.config['DROPZONE_ALLOWED_FILE_TYPE'] = 'image/*, .pdf, .txt'複製程式碼
對於平行上傳、CSRF保護等內容的具體實現方法你可以參考文件瞭解。不過,這個專案目前還沒有建立完善的文件,暫時只是寫到README裡,如果你發現了英文語法或拼寫錯誤,歡迎指正,同時也歡迎為專案貢獻程式碼。
示例程式
Flask-Dropzone的Git倉庫中的examples目錄下包含4個示例程式,分別演示了基本用法、CSRF保護、平行上傳和上傳完成後跳轉四個功能。
另外,helloflask倉庫裡在demos/form目錄下的示例程式也包含一個Flask-Dropzone使用示例。
相關連結
- GitHub:github.com/greyli/flas…