Flask文件上傳(二):使用擴展實現

上篇文章介紹了用Flask原生實現上傳功能的過程。閱讀這篇文章前,確保你已經讀過了上篇文章。為了便於理解,這篇文章的章節安排和上一篇基本相同。

這次介紹使用擴展Flask-Uploads實現上傳功能。

Flask文件上傳系列目錄

  1. Flask文件上傳(一):原生實現

  2. Flask文件上傳(二):使用擴展實現

  3. Flask文件上傳(三):完整實現

  4. Flask文件上傳(四):文件管理與多文件上傳
  5. 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地址:gist.github.com/greyli/

大型項目配置

有同學問到這個,補充一下。在大型項目里,可以像其他擴展一樣,在程序工廠函數里初始化,在表單文件里導入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文檔:pythonhosted.org/Flask-

  • Flask-Uploads項目地址:github.com/maxcountryma

- - - - -

更多關於Flask和Web開發的原創內容,歡迎關注知乎專欄 - Hello, Flask!。

推薦閱讀:

上雲連載5:使用 Nginx + uWSGI 部署 Flask 應用
Flask實踐:計算器
flask框架中應用上下文跟請求上下文是什麼意思?

TAG:Python | Flask | 文件上传 |