上传文件
一个带有 的
<form>
标记,标记中含有一个<input type=file>
。应用通过请求对象的
files
字典来访问文件。使用文件的
save()
方法把文件永久地保存在文件系统中。
让我们从一个基本的应用开始,这个应用上传文件到一个指定目录,并把文件显示给用户。以下是应用的前导代码:
首先我们导入了一堆东西,大多数是浅显易懂的。werkzeug.secure_filename()
会在稍后解释。 UPLOAD_FOLDER
是上传文件要储存的目录, ALLOWED_EXTENSIONS
是允许上传的文件扩展名的集合。
为什么要限制文件件的扩展名呢?如果直接向客户端发送数据,那么你可能不会想让用户上传任意文件。否则,你必须确保用户不能上传 HTML 文件,因为 HTML 可能引起 XSS 问题(参见 )。如果服务器可以执行 PHP 文件,那么还必须确保不允许上传 .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':
if 'file' not in request.files:
flash('No file part')
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
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))
return redirect(url_for('uploaded_file',
filename=filename))
<!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>
'''
进一步说明
你可以会好奇 secure_filename()
做了哪些工作,如果不使用它会有什么后果。假设有人把下面的信息作为 filename 传递给你的应用:
假设 ../
的个数是正确的,你会把它和 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 版本开始我们可以使用一个函数来完成这个任务:
另外,可以把 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, {
})
如果你现在运行应用,那么应该一切都应该按预期正常工作。
New in version 0.6.
Flask 到底是如何处理文件上传的呢?如果上传的文件很小,那么会把它们储存在内存中。否则就会把它们保存到一个临时的位置(通过 可以得到这个位置)。但是,如何限制上传文件的尺寸呢?缺省情况下, Flask 是不限制上传文件的尺寸的。可以通过设置配置的 来限制文件尺寸:
上面的代码会把尺寸限制为 16 M 。如果上传了大于这个尺寸的文件, Flask 会抛出一个 RequestEntityTooLarge
异常。
连接重置问题
当使用本地开发服务器时,可能会得到一个连接重置,而不是一个 413 响应。在生产 WSGI 服务器上运行应用时会得到正确的响应。
Flask 0.6 版本中添加了这个功能。但是通过继承请求对象,在较老的版本中也可以实现这个功能。更多信息请参阅 Werkzeug 关于文件处理的文档。
在不久以前,许多开发者是这样实现上传进度条的:分块读取上传的文件,在数据库中储存上传的进度,然后在客户端通过 JavaScript 获取进度。简而言之,客户端每5 秒钟向服务器询问一次上传进度。觉得讽刺吗?客户端在明知故问。
因为所有应用中上传文件的方案基本相同,因此可以使用 扩展来实现文件上传。这个扩展实现了完整的上传机制,还具有白名单功能、黑名单功能以及其他功能。