FLask上傳文件


Flask上傳文件

文件上傳的基本原理實際上很簡單,基 本上是:

  1. 一個帶有 enctype=multipart/form-data<form> 標記,標記中含有 一個 <input type=file>
  2. 應用通過請求對象的 files 字典來訪問文件。
  3. 使用文件的 save() 方法把文件 永久地保存在文件系統中。

例,上傳文件html頁:

<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" name="img">
    <input type="submit" value="上傳">
</form>

讓我們從一個基本的應用開始,這個應用上傳文件到一個指定目錄,並把文件顯示給 用戶。

以下是應用的前導代碼:

import os,sys
from flask import Flask, flash, request, redirect, url_for
from werkzeug.utils import secure_filename

# 為了便於遷移,上傳文件的路徑使用了os模塊來尋找當前文件夾拼接windows文件分隔符再拼接真正的目錄名,
# 例如我的就是uploads,最后再加上一個文件分隔符即可。
UPLOAD_FOLDER =os.path.curdir+os.path.sep+'uploads'+os.path.sep
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

首先我們導入了一堆東西,大多數是淺顯易懂的。 werkzeug.secure_filename() 會在稍后解釋。 UPLOAD_FOLDER 是上傳文 件要儲存的目錄, ALLOWED_EXTENSIONS 是允許上傳的文件擴展名的集合。

注:為了便於遷移,上傳文件的路徑我使用了os模塊來尋找當前文件夾拼接windows文件分隔符再拼接真正的目錄名。例如我的就是uploads,最后再加上一個文件分隔符即可。

python中os.path常用模塊:

os.path.sep -->windows路徑分隔符 
os.path.altsep -->linux下就用:'/' 

根目錄:os.path.curdir
當前目錄:os.path.pardir
父目錄:os.path.abspath(path)
絕對路徑:os.path.join()

常用來鏈接路徑:os.path.split(path),把path分為目錄和文件兩個部分,以列表返回。
  • 為什么要限制文件的擴展名呢?

如果直接向客戶端發送數據,那么你可能不會想讓用戶上傳任意文件。否則,你必須確保用戶不能上傳 HTML 文件,因為 HTML 可能引 起 XSS 問題(參見 跨站腳本攻擊(XSS) )。如果服務器可以執行 PHP 文件,那么還必須確保不允許上傳 .php 文件。

下一個函數檢查擴展名是否合法,上傳文件,把用戶重定向到已上傳文件的 URL:

def allowed_file(filename):
    # 獲取文件擴展名,以'.'為右分割然后取第二個值
    return '.' in filename and \
        filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

@app.route('/', methods=['GET','POST'])
def upload_file():
    if request.method == 'POST':
        # check if the post request has the file part
        if 'file' not in request.files:
            flash('No file part')
            return redirect(request.url)
        file = request.files['file']
        # 如果沒有選擇文件,瀏覽器也可以提交一個沒有文件名的空部分
        if file.filename == '':
            flash('No selected file')
            return redirect(request.url)
        # 如果有文件且文件擴展名允許
        if file and allowed_file(file.filename):
           filename = secure_filename(file.filename)
           # 拼接保存文件路徑
           file.save(os.path.join(app.config['UPLOAD_FOLDER'],filename))
           # 返回反向解析得到的URL
           return redirect(url_for('uploaded_file', filename=filename))
    return '''
    <!doctype html>
    <title>Upload new File</title>
    <h1>Upload new File</h1>
    <form method=post enctype=multipart/form-data>
      <input type=file name=file>
      <input type=submit value=Upload>
    </form>
    '''

那么 secure_filename() 函數到底是有什么用?

有一條原則是“永遠不要信任用戶輸入”

這條原則同樣適用於已上傳文件的文件名。所有提交的表單數據可能是偽造的,文件名也可以是危險的。此時要謹記:在把文件保存到文件系統之前總是要使用secure_filename這個函數對文件名進行安檢。

進一步說明

你可以會好奇 secure_filename() 做了哪些工作,如果 不使用它會有什么后果。假設有人把下面的信息作為 filename 傳遞給你的應 用:

filename = "../../../../home/username/.bashrc"

假設 ../ 的個數是正確的,你會把它和 UPLOAD_FOLDER 結合在一起,那 么用戶就可能有能力修改一個服務器上的文件,這個文件本來是用戶無權修改的。 這需要了解應用是如何運行的,但是請相信我,黑客都是很變態的 😃

現在來看看函數是如何工作的:

>>> secure_filename('../../../../home/username/.bashrc')
'home_username_.bashrc'

現在還剩下一件事:為已上傳的文件提供服務。在 upload_file() 中,我 們把用戶重定向到 url_for('uploaded_file', filename=filename) ,即 /uploads/filename

因此我們寫一個 uploaded_file() 來返回該文件 名稱。

Flask 0.5 版本開始我們可以使用一個函數來完成這個任務:

from flask import send_from_directory

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'],
                               filename)

另外,可以把 uploaded_file 注冊為 build_only 規則,並使用 SharedDataMiddleware

這種方式可以在 Flask 老版本 中使用:

from werkzeug import SharedDataMiddleware
app.add_url_rule('/uploads/<filename>', 'uploaded_file',
                 build_only=True)
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
    '/uploads':  app.config['UPLOAD_FOLDER']
})

如果你現在運行應用,那么應該一切都應該按預期正常工作。

flask_uploads

要注意的是,目前應用下,你上傳的圖片文件名要為英文。中文的話,會出現彈框問你保存還是打開……

改進上傳

Flask 到底是如何處理文件上傳的呢?

  • 如果上傳的文件很小,那么會把它們儲存在內存中。

  • 否則就會把它們保存到一個臨時的位置(通過 tempfile.gettempdir() 可以得到這個位置)。

如何限制上傳文件的尺寸呢?

缺省情況下, Flask 是不限制上傳文件的尺寸的。可以通過設置配置的 MAX_CONTENT_LENGTH 來限制文 件尺寸:

from flask import Flask, Request

app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

上面的代碼會把尺寸限制為 16 M 。如果上傳了大於這個尺寸的文件, Flask 會拋 出一個 RequestEntityTooLarge 異常。

連接重置問題

當使用本地開發服務器時,可能會得到一個連接重置,而不是一個 413 響應。 在生產 WSGI 服務器上運行應用時會得到正確的響應。

Flask 0.6 版本中添加了這個功能。但是通過繼承請求對象,在較老的版本中也可以實現這個功能。更多信息請參閱 Werkzeug 關於文件處理的文檔。

上傳進度條

在不久以前,許多開發者是這樣實現上傳進度條的:分塊讀取上傳的文件,在數據庫 中儲存上傳的進度,然后在客戶端通過 JavaScript 獲取進度。簡而言之,客戶端每 5 秒鍾向服務器詢問一次上傳進度。覺得諷刺嗎?客戶端在明知故問。

一個更簡便的方案

現在有了更好的解決方案,更快且更可靠。像 jQuery 之類的 JavaScript 庫包含的輕松構建進度條的插件。

因為所有應用中上傳文件的方案基本相同,因此可以使用 Flask-Uploads 擴展來實現文件上傳。這個擴展實現了完整的上傳機制,還具有白名單功能、黑名單功能以及其他功能。

文:鐵樂與貓

2018-9-6

參考引用

Flask官網中文文檔-上傳文件


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM