W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
哦,上傳文件可是個經(jīng)典的好問題了。文件上傳的基本概念實際上非常簡單, 他基本是這樣工作的:
讓我們建立一個非常基礎的小應用,這個小應用可以上傳文件到一個指定的文件夾里, 然后將這個文件顯示給用戶。讓我們看看這個應用的基礎代碼:
import os
from flask import Flask, request, redirect, url_for
from werkzeug import secure_filename
UPLOAD_FOLDER = '/path/to/the/uploads'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
首先我們導入一些東西,大多數(shù)內(nèi)容都是直接而容易的。werkzeug.secure_filename() 將會在稍后進行解釋。 UPLOAD_FOLDER 是我們儲存上傳的文件的地方,而 ALLOWED_EXTENSIONS 則是允許的文件類型的集合。然后我們手動為應用添加一個的 URL 規(guī)則。我們 通常很少這樣做,但是為什么這里要如此呢?原因是我們希望實際部署的服務器 (或者我們的開發(fā)服務器)來為我們提供這些文件的訪問服務,所以我們只需要 一個規(guī)則用來生成指向這些文件的 URL 。
為什么我們限制上傳文件的后綴呢?您可能不希望您的用戶能夠上傳任何文件 到服務器上,如果服務器直接將數(shù)據(jù)發(fā)送給客戶端。以這種方式,您可以確保 您的用戶不能上傳可能導致 XSS 問題(參考 跨站腳本攻擊(XSS) )的 HTML 文件。也 確保會阻止 .php 文件以防其會被運行。當然,誰還會在服務器上安裝 PHP 啊,是不是? :)
下一步,就是檢查文件類型是否有效、上傳通過檢查的文件、以及將用戶重定向到 已經(jīng)上傳好的文件 URL 處的函數(shù)了:
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>
'''
那么 secure_filename() 函數(shù)具體做了那些事呢?現(xiàn)在的問題 是,有一個信條叫做“永遠別相信你用戶的輸入” ,這句話對于上傳文件的文件名也是同樣 有效的。所有提交的表單數(shù)據(jù)都可以偽造,而文件名本身也可能是危險的。在攝氏只需記住: 在將文件保存在文件系統(tǒng)之前,要堅持使用這個函數(shù)來確保文件名是安全的。
關于文件名安全的更多信息
您對 secure_filename() 的具體工作和您沒使用它會造成的后果 感興趣?試想一個人可以發(fā)送下列信息作為 filename 給您的應用:
filename = "../../../../home/username/.bashrc"
假定 ../ 的數(shù)量是正確的,而您會將這串字符與 UPLOAD_FOLDER 所指定的 路徑相連接,那么這個用戶就可能有能力修改服務器文件系統(tǒng)上的一個文件,而他 不應該擁有這種權限。這么做需要一些關于此應用情況的技術知識,但是相信我, 駭客們都有足夠的耐心 :)
現(xiàn)在我們來研究一下這個函數(shù)的功能:
>>> secure_filename('../../../../home/username/.bashrc')
'home_username_.bashrc'
現(xiàn)在還有最后一件事沒有完成: 提供對已上傳文件的訪問服務。 在 Flask 0.5 以上的版本我們可以使用一個函數(shù)來實現(xiàn)此功能:
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 規(guī)則,然后使用 SharedDataMiddleware 類來實現(xiàn)下載服務。這種方法 同時支持更老版本的 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']
})
運行應用,不出意外的話,一切都應該像預期那樣工作了。
0.6 新版功能.
Flask 到底是如何處理上傳的呢?如果服務器相對較小,那么他會先將文件儲存在 網(wǎng)頁服務器的內(nèi)存當中。否則就將其寫入一個臨時未知(如函數(shù) tempfile.gettempdir() 返回的路徑)。但是怎么指定一個文件大小的上限,當文件大于此限制,就放棄 上傳呢? 默認 Flask 會很歡樂地使用無限制的空間,但是您可以通過在配置中設定 MAX_CONTENT_LENGTH 鍵的值來限制它:
from flask import Flask, Request
app = Flask(__name__)
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
上面的代碼將會把上傳文件限制為最大 16 MB 。 如果請求傳輸一個更大的文件, Flask 會拋出一個 RequestEntityTooLarge 異常。
這個特性是在 Flask 0.6 中被加入的,但是更老的版本也可以通過構建請求對象 的子類來實現(xiàn)。更多信息請查詢 Werkzeug 文檔中文件處理部分的內(nèi)容。
以前,很多開發(fā)者實現(xiàn)進度條的方法是這樣的: 一邊小塊小塊地讀取傳輸來的文件, 一邊將上傳進度儲存在數(shù)據(jù)庫中,然后在通過客戶端的 JavaScript 代碼讀取進度。 簡單來說,客戶端會每5秒鐘詢問服務器傳輸?shù)倪M度。您感覺到這種諷刺了么?客戶端 詢問一些他本應該已經(jīng)知道的事情。
現(xiàn)在有了一些性能更好、運行更可靠的解決方案。WEB 已經(jīng)有了不少變化,現(xiàn)在您可以 使用 HTML5、Java、Silverlight 或者 Flash 來實現(xiàn)客戶端更好的上傳體驗。看一看 下面列出的庫的連接,可以找到一些很好的樣例。
因為存在一個處理上傳文件的范式,這個范式在大多數(shù)應用中機會不會有太大改變, 所以 Flask 存在一個擴展名為 Flask-Uploads ,這個擴展實現(xiàn)了一整套成熟的 文件上傳架構。它提供了包括文件類型白名單、黑名單等多種功能。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: