延續上一篇文章,本文將介紹HTML表單的上傳檔案欄位的參數以及接收和儲存上傳檔的基本Python Flask程式。
上傳檔案的HTML表單網頁
底下是上傳檔案的表單外觀和HTML碼,<input type=”file”;>標籤元素會在Chrome瀏覽器上呈現「選擇檔案」鈕,此欄位的名稱可自行設定,這個例子將它命名成”filename”。
<form>標籤的enctype屬性的兩個常用值如下:
- application/x-www-form-urlencoded:用於傳送不含檔案資料的文字表單。此為預設值,所以enctype=”application/x-www-form-urlencoded”可省略不寫。
- multipart/form-data:用於傳送包含「檔案」欄位的表單資料。multipart代表「多個部份」,也就是上傳檔案將被自動分割成數個、分批上傳。
從HTML表單的「檔案」欄位篩選上傳檔案類型
定義「檔案」欄位的 <input type=”file”> 標籤,除了設定欄位名稱的name屬性,還有下列常用的屬性:
- required:代表此欄位是「必填」。
- multiple:代表這個欄位可接收多個檔案,設定範例:
<input type="file" name="filename" multiple>
- accept:設定允許上傳的檔案類型。底下的設定僅允許上傳.doc和.docx檔,副檔名之間用逗號隔開:
<input type="file" name="filename" accept=".doc, .docx">
底下的設定僅允許上傳.png, .jpg和.gif圖檔:
<input type="file" name="filename" accept=".png, .jpg, .gif">
另一種寫法是透過MIME類型篩選上傳檔案格式(參閱第11章),底下的檔案欄位僅允許上傳影像圖檔:
<input type="file" name="filename" accept="image/*">
影像、視訊以及聲音的MIME類型設置如下:
- 代表任意影像檔:”image/*”
- 代表任意視訊檔:”video/*”
- 代表任意聲音檔:”audio/*”
副檔名和MIME可合併使用,底下的檔案欄位僅接受上傳影像以及Word文件:
<input type="file" name="filename" accept="image/*, .doc, .docx ">
包含一個「檔案」欄位的表單的完整網頁(Flask網頁樣版)HTML碼如下,此檔案欄位是必填、僅接受影像圖檔:
<!doctype html> <html> <head> <meta charset="utf-8"> <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}"> <title>上傳影像檔</title> </head> <body> <h1>上傳影像檔</h1> <form method="POST" enctype="multipart/form-data" action="{{ url_for('upload_file') }}"> <input type="file" name="filename" accept="image/*" required> <input type="submit" value="上傳"> </form> </body> </html>
筆者把處理表單的Python函式命名成upload_file(),所以表單的action屬性設成”{{ url_for(‘upload_file’) }}”。
建立Python Flask網站服務的虛擬環境
下文將透過Python Flask程式接收上傳檔案,為了方便管理專案程式,請先建立Python虛擬環境。筆者在D磁碟機根目錄新增一個photoshare資料夾,然後執行左下的命令建立虛擬環境,接著新增src和templates,以及static, uploads等資料夾,相關說明請參閱《超圖解Python程式設計入門》第10章「佈署網站到雲端空間」。
取得「上傳資料夾」的絕對路徑
此範例的Python程式將把上傳檔存入static/uploads路徑。上傳檔的儲存路徑必須是「絕對路徑」,Python 3的pathlib「路徑程式庫」裡的Path()函式可傳回檔案的絕對路徑。
底下是在虛擬環境的src資料夾中的app.py檔,執行Path()函式所傳回的絕對路徑:
假設你的Python虛擬環境設置在macOS或Linux系統的使用者家目錄,執行結果將類似這樣:
底下的程式片段將把src/static/uploads的絕對路徑(此例為:”D:\photoshare\src\static\uploads”)存入UPLOAD_FOLDER變數:
import os import pathlib # 取得目前檔案所在的資料夾 SRC_PATH = pathlib.Path(__file__).parent.absolute() # 結合目前的檔案路徑和static及uploads路徑 UPLOAD_FOLDER = os.path.join(SRC_PATH, 'static', 'uploads')
基本的Python Flask檔案上傳處理程式
底下是接收、儲存上傳檔的Python Flask程式,筆者將它命名成app.py,存入虛擬環境的src資料夾:
import os import pathlib from flask import Flask, url_for, redirect, render_template, request # 取得目前檔案所在的資料夾 SRC_PATH = pathlib.Path(__file__).parent.absolute() UPLOAD_FOLDER = os.path.join(SRC_PATH, 'static', 'uploads') app = Flask(__name__) @app.route('/', methods=['GET']) def index(): return render_template('index.html') @app.route('/', methods=['POST']) def upload_file(): file = request.files['filename'] if file.filename != '': file.save(os.path.join(UPLOAD_FOLDER, file.filename)) return redirect(url_for('index')) if __name__ == "__main__": app.run()
執行此Python程式後,透過瀏覽器開啟本機網站(127.0.0.1:5000),底下的路由敘述將被執行,傳送index.html網頁給瀏覽器:
當使用者按下表單的「上傳」鈕,底下的路由敘述將被執行,接收上傳的檔案並存入src/static/uploads路徑,然後把瀏覽器重新導向到首頁。
以上是個簡單的Python Flask上傳檔案服務,它有兩個缺點:
- 雖然HTML表單已設置僅限上傳圖檔,但使用者仍舊可以選取任意檔案並且上傳。
- 上傳檔案之後,沒有顯示任何訊息就跳回首頁。
下一篇文章將解決這兩個問題。