Flask 檔案上傳到伺服器的方法 (1)

陳小痘
8 min readJan 8, 2020

--

這周筆者接到『檔案上傳』的工作任務,所以在這邊除了做筆記之外,也分享一下我的學習心得,希望可以幫助到對此有興趣的讀者。那麼抓緊囉!我們來開始吧^ ^

檔案上傳的基本概念

檔案上傳的基本概念其實很簡單, 它基本運作流程是這樣的:
(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 上傳多個檔案』,如果讀者有興趣的話可以好好期待一下哦!那麼希望這一次分享有幫助到大家,下一篇再見啦~~~

--

--