使用Flask-Dropzone在Flask程式中實現檔案上傳

greylihui發表於2018-06-25
某天在Stack Overflow上看到一個關於Dropzone.js的問題,研究之後寫了一個回答。我發現,如果有一個整合Dropzone.js到Flask,並且簡化設定步驟的擴充套件,肯定要比其他上傳方式簡單的多——於是就有了Flask-Dropzone

使用Flask-Dropzone在Flask程式中實現檔案上傳

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>複製程式碼
上傳區域的截圖示例如下所示:
使用Flask-Dropzone在Flask程式中實現檔案上傳

在伺服器端處理並儲存上傳檔案

當檔案被拖拽到上傳區域,或是點選上傳區域選擇上傳檔案後,這些檔案會以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在Flask程式中實現檔案上傳

完整的配置列表

Flask-Dropzone提供了豐富的配置變數,你可以使用它們對Dropzone.js進行各類配置。很遺憾掘金的編輯器不支援表格,所以這裡只能插入一張圖片,如果你需要文字,可以訪問這篇文章
使用Flask-Dropzone在Flask程式中實現檔案上傳

這些配置的用法你可以參考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'] = True
app.config['DROPZONE_ALLOWED_FILE_TYPE'] = 'image/*, .pdf, .txt'複製程式碼
對於平行上傳、CSRF保護等內容的具體實現方法你可以參考文件瞭解。不過,這個專案目前還沒有建立完善的文件,暫時只是寫到README裡,如果你發現了英文語法或拼寫錯誤,歡迎指正,同時也歡迎為專案貢獻程式碼。

示例程式

Flask-Dropzone的Git倉庫中的examples目錄下包含4個示例程式,分別演示了基本用法、CSRF保護、平行上傳和上傳完成後跳轉四個功能。
另外,helloflask倉庫裡在demos/form目錄下的示例程式也包含一個Flask-Dropzone使用示例。

相關連結


相關文章