使用Flask-Dropzone在Flask程序中實現文件上傳
來自專欄 Hello, Flask!25 人贊了文章
某天在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 Dropzoneapp = Flask(__name__)dropzone = Dropzone(app)
在使用工廠函數創建程序實例時,你也可以使用init_app()
方法:
from flask_dropzone import Dropzonedropzone = 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 osfrom flask import Flask, requestfrom flask_dropzone import Dropzoneapp = 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-Drozone的文檔或是示常式序了解,這裡我們僅簡單介紹一下對文件類型進行過濾的設置方法。
設置文件類型過濾
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] = Trueapp.config[DROPZONE_ALLOWED_FILE_TYPE] = image/*, .pdf, .txt
對於平行上傳、CSRF保護等內容的具體實現方法你可以參考文檔了解。不過,這個項目目前還沒有創建完善的文檔,暫時只是寫到README里,如果你發現了英文語法或拼寫錯誤,歡迎指正,同時也歡迎為項目貢獻代碼。
示常式序
Flask-Dropzone的Git倉庫中的examples目錄下包含4個示常式序,分別演示了基本用法、CSRF保護、平行上傳和上傳完成後跳轉四個功能。
另外,helloflask倉庫里在demos/form
目錄下的示常式序也包含一個Flask-Dropzone使用示例。
相關鏈接
- GitHub:https://github.com/greyli/flask-dropzone
- PyPI:https://pypi.python.org/pypi/Flask-Dropzone
推薦閱讀: