檔案上傳的基本概念
檔案上傳的基本概念其實很簡單, 它基本運作流程是這樣的:
(1) 在前端網頁 (HTML),會有一個 <form>
標籤,標籤有個屬性參數 enctype=multipart/form-data
。並且在標籤裡面包含 <input type=file>
。
(2) Server 上的 Web API 通過 request.files['file']
訪問檔案。
(3) 使用 file.save(檔案上傳路徑+檔名)
將檔案上傳到指定的路徑。
以上,是檔案上傳的基本概念。接下來會開始配合程式做詳細地講解,如果讀者對於 Flask 還不太熟悉,請到:使用 Flask 創建 Web API (筆記),這是筆者之前寫的第一篇 Flask 運用的文章,希望可以對於新手有所幫助!
Flask 單一檔案上傳
好的!我們來開始吧~剛開始接觸,就以簡單扼要的程式來講解。所以只會有一支程式檔 (app.py),在本地運作,一次上傳一份檔案。筆者會把程式依內容拆分開來說明。
首先, import 我們會使用到的模組。UPLOAD_FOLDER
是檔案上傳的路徑。ALLOWED_EXTENSIONS
則是設定限制檔案的格式,不在限制裡面的檔案格式將無法上傳。MAX_CONTENT_LENGTH
,該設定可以限制檔案大小,如果超過檔案大小限制,Flask 將會拋出 RequestEntityTooLarge 的例外錯誤。
import os
from flask import Flask, request, redirect, url_for
from werkzeug.utils import secure_filename
UPLOAD_FOLDER = 'D:/_test/temp'
ALLOWED_EXTENSIONS = set(['pdf', 'png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB
為了確保上傳檔案的安全性 (例如使用者可能上傳病毒或其他奇怪檔案),所以我們在 allowed_file()
函式中,做檔案的副檔名檢查。
接著 upload_file()
函式中用 request.files['file']
來取得使用者上傳的檔案。其 request.files 的 key 值 'file'
對應到的就是程式倒數第四行 <input type=file name=file>
中的 name=file
。
通過 allowed_file()
副檔名檢查之後,由於要將檔案存至檔案系統 (file system) 中,所以我們要將檔名以 secure_filename()
函式進行過濾,避免檔名為類似 /../../../filename
的 Directory traversal attack 發生。
<form>
標籤裡的 enctype 屬性一定要填寫 multipart/form-data 才能上傳檔案。因為早期 HTTP POST 是不支持檔案上傳的,所以帶給編程開發不少麻煩,後來 Content-Type 擴充了multipart/form-data 來支持向 Server 發送二進制數據,實現 HTTP POST 的檔案上傳。
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
@app.route('/', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
file = request.files['file']
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'],
filename))
return redirect(url_for('uploaded_file',
filename=filename))
return '''
<!doctype html>
<title>Upload new File</title>
<h1>Upload new File</h1>
<form action="" method=post enctype=multipart/form-data>
<p><input type=file name=file>
<input type=submit value=Upload>
</form>
'''
接著補充一項功能:提供對已上傳檔案的訪問服務。
在 Flask 0.5 以上的版本,我們可以使用 send_from_directory 這個模組來實現此功能。在上面的程式區塊 return redirect(url_for('uploaded_file', filename=filename))
就是當檔案上傳完,會跳轉到 uploaded_file()
函式,並帶著參數 filename
。而我們會注意到 @app.route('/uploads/<filename>')
的 '/uploads/<filename>'
,代表會在以 <filename>
標記的網址,開啟已上傳的檔案。
from flask import send_from_directory
@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
最後,if __name__ == ‘main’
是當我們拿 app.py 當作主程式執行時,Python 預設變數 __name__ 會是 ‘__main__’,所以會執行 app.run(debug=True)
,讓 Web API 跑起來;反之,如果 app.py
非當主程式,Web API 就不會跑起來了。
if __name__ == '__main__':
app.run(debug=True)
運作成果!
我們 Web API (app.py) 完成後,就來測試看看吧~
先開啟 cmd (命令提示字元),並輸入 app.py 所在位置,執行。
Web API 就會在本地 Run 起來。
因為我們是在本地跑 Web API,所以請在瀏覽器輸入預設的 localhost 和 host (127.0.0.1:5000)。接著我們就會看到簡約的 Upload 網頁,請按下:選擇檔案。
按完:選擇檔案,即會開啟視窗供使用者選擇欲上傳的檔案。
目前 Web API 功能只能上傳單一檔案,所以筆者就索性選擇了單張 PNG 圖片檔,並按下:開啟。
我們就會看到原本字樣的改變:未選擇任何檔案 --> FFVII_Remake.png,變成筆者剛剛選的檔案名稱。最後我們按下:Upload。
我們會看到跳轉至其他網頁,其網址就是我們之前程式解說有提及到的:/uploads/<filename>
。而且此網頁除了顯示圖片外,使用者也可以在這邊下載圖片至本機電腦 (雖然圖片是我們剛剛上傳的 XD)。
我們到檔案上傳的路徑來做檢查 UPLOAD_FOLDER = 'D:/_test/temp'
,的確存放著 FFVII_Remake.png 圖檔,代表 Upload File 成功!
這次筆者把『如何在 Flask 上傳單一檔案』說明了一遍,下一篇將會介紹『在 Flask 上傳多個檔案』,如果讀者有興趣的話可以好好期待一下哦!那麼希望這一次分享有幫助到大家,下一篇再見啦~~~