Flask文件上傳(二):使用擴展實現
這次介紹使用擴展Flask-Uploads實現上傳功能。
Flask文件上傳系列目錄
- Flask文件上傳(一):原生實現
- Flask文件上傳(二):使用擴展實現
- Flask文件上傳(三):完整實現
- Flask文件上傳(四):文件管理與多文件上傳
- Flask文件上傳(五):拖拽上傳和進度條
Flask-Uploads
Flask-Uploads幫你簡化了大部分操作。比如在上篇文章中,我們需要自己設置一個集合來設置允許哪些類型的文件(ALLOWED_EXTENSIONS),而Flask-Uploads已經把常用的文件類型分好類,你只需要導入相應的集合名稱,比如IMAGES、TEXT、AUDIO等等(默認配置為DEFAULTS,包括TEXT + DOCUMENTS + IMAGES + DATA)。除此之外,你還可以配置全部允許(All)、除某些文件類型外全允許(AllExcept())。這些集合也可以組合使用(比如IMAGES+TEXT)。
上傳配置
因為Flask-Uploads允許你創建不同的set(代表你上傳的文件的集合),所以配置時要把下面的FILES替換成你的set名。
比如文件儲存地址設置:
UPLOADED_FILES_DEST = "/path/to/the/uploads"
如果你的set叫photos,那麼這條設置就改成:
UPLOADED_PHOTOS_DEST = "/path/to/the/uploads"
你也可以設置一個默認的配置:
UPLOADS_DEFAULT_DEST = "/path/to/the/uploads"
更多的可用配置見文檔。
安全問題
1、文件類型過濾
創建一個set(通過實例化UploadSet()類實現),然後使用configure_uploads()方法註冊並完成相應的配置(類似大多數擴展提供的初始化類),傳入當前應用實例和set:
photos = UploadSet("photos", IMAGES)configure_uploads(app, photos)
這裡的photos是set的名字,它很重要。因為接下來它就代表你已經保存的文件,對它調用save()方法保存文件,對它調用url()獲取文件url,對它調用path()獲取文件的絕對地址……(你可以把它類比成代表資料庫的db)
2、文件名過濾
不再需要secure_filename()函數來處理文件名,使用set的save()方法直接保存從request對象獲取的文件,將返回處理後的文件名:
filename = photos.save(request.files["photo"])
可能還需要再提醒一下,這裡的["photo"]是input欄位的name值。
3、限制文件大小
導入patch_request_class()函數,傳入應用實例和大小(默認為16MB),比如:
patch_request_class(app)
或是
patch_request_class(app, 32 * 1024 * 1024) # 或是從配置里讀取大小
獲取文件
你不必再創建新的視圖函數來獲取文件,Flask-Uploads自帶了一個視圖函數,當你對一個set(比如我們上面創建的photos)使用url()方法(傳入文件名作為參數),比如:
photos.url("demo.jpg")
它會返回一個文件的url:
http://example.com/_uploads/photos/demo.jpg # /<setname>/<path:filename>
完整實現
同樣是一個圖片上傳的demo,這次要簡單的多。
# -*- coding: utf-8 -*-import osfrom flask import Flask, requestfrom flask_uploads import UploadSet, configure_uploads, IMAGES, patch_request_classapp = Flask(__name__)app.config["UPLOADED_PHOTOS_DEST"] = os.getcwd() # 文件儲存地址photos = UploadSet("photos", IMAGES)configure_uploads(app, photos)patch_request_class(app) # 文件大小限制,默認為16MBhtml = """ <!DOCTYPE html> <title>Upload File</title> <h1>圖片上傳</h1> <form method=post enctype=multipart/form-data> <input type=file name=photo> <input type=submit value=上傳> </form> """@app.route("/", methods=["GET", "POST"])def upload_file(): if request.method == "POST" and "photo" in request.files: filename = photos.save(request.files["photo"]) file_url = photos.url(filename) return html + "<br><img src=" + file_url + ">" return htmlif __name__ == "__main__": app.run()
註:視圖函數里的兩個『photo』均指的是input欄位的name值。
Gist地址:https://gist.github.com/greyli/addff01c19ddca78cddf386800e57045
大型項目配置
有同學問到這個,補充一下。在大型項目里,可以像其他擴展一樣,在程序工廠函數里初始化,在表單文件里導入set。
__init__.py
# -*-coding: utf-8-*-from flask import Flaskfrom flask_bootstrap import Bootstrapfrom flask_mail import Mailfrom flask_moment import Momentfrom flask_sqlalchemy import SQLAlchemyfrom flask_login import LoginManagerfrom config import configfrom flask_uploads import UploadSet, configure_uploads, IMAGES # 導入bootstrap = Bootstrap()mail = Mail()moment = Moment()db = SQLAlchemy()photos = UploadSet("photos", IMAGES) # 創建setdef create_app(config_name): app = Flask(__name__) app.config.from_object(config[config_name]) config[config_name].init_app(app) bootstrap.init_app(app) mail.init_app(app) moment.init_app(app) db.init_app(app) configure_uploads(app, photos) # 初始化 return app
在forms.py或views.py中導入set:
from .. import photos
怎麼導入視你的目錄結構而定,這裡的目錄結構如下,photos放在__init__.py文件里:
app/ - __init__.py - main/ - forms.py - ...
相關鏈接
- Flask-Uploads文檔:http://pythonhosted.org/Flask-Uploads/
- Flask-Uploads項目地址:https://github.com/maxcountryman/flask-uploads
- - - - -
更多關於Flask和Web開發的原創內容,歡迎關注知乎專欄 - Hello, Flask!。
推薦閱讀:
※上雲連載5:使用 Nginx + uWSGI 部署 Flask 應用
※Flask實踐:計算器
※flask框架中應用上下文跟請求上下文是什麼意思?