.. _uploading-files:
上传文件
===============
哦,上传文件可是个经典的好问题了。文件上传的基本概念实际上非常简单,
他基本是这样工作的:
1. 一个 ``
'''
那么 :func:`~werkzeug.utils.secure_filename` 函数具体做了那些事呢?现在的问题
是,有一个信条叫做“永远别相信你用户的输入” ,这句话对于上传文件的文件名也是同样
有效的。所有提交的表单数据都可以伪造,而文件名本身也可能是危险的。在摄氏只需记住:
在将文件保存在文件系统之前,要坚持使用这个函数来确保文件名是安全的。
.. admonition:: 关于文件名安全的更多信息
您对 :func:`~werkzeug.utils.secure_filename` 的具体工作和您没使用它会造成的后果
感兴趣?试想一个人可以发送下列信息作为 `filename` 给您的应用::
filename = "../../../../home/username/.bashrc"
假定 ``../`` 的数量是正确的,而您会将这串字符与 `UPLOAD_FOLDER` 所指定的
路径相连接,那么这个用户就可能有能力修改服务器文件系统上的一个文件,而他
不应该拥有这种权限。这么做需要一些关于此应用情况的技术知识,但是相信我,
骇客们都有足够的耐心 :)
现在我们来研究一下这个函数的功能:
>>> secure_filename('../../../../home/username/.bashrc')
'home_username_.bashrc'
现在还有最后一件事没有完成: 提供对已上传文件的访问服务。 在 Flask 0.5
以上的版本我们可以使用一个函数来实现此功能::
from flask import send_from_directory
@app.route('/uploads/')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
或者,您也可以选择为 `uploaded_file` 注册 `build_only` 规则,然后使用
:class:`~werkzeug.wsgi.SharedDataMiddleware` 类来实现下载服务。这种方法
同时支持更老版本的 Flask::
from werkzeug import SharedDataMiddleware
app.add_url_rule('/uploads/', 'uploaded_file',
build_only=True)
app.wsgi_app = SharedDataMiddleware(app.wsgi_app, {
'/uploads': app.config['UPLOAD_FOLDER']
})
运行应用,不出意外的话,一切都应该像预期那样工作了。
改进上传功能
-----------------
.. versionadded:: 0.6
Flask 到底是如何处理上传的呢?如果服务器相对较小,那么他会先将文件储存在
网页服务器的内存当中。否则就将其写入一个临时未知(如函数 :func:`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 会抛出一个 :exc:`~werkzeug.exceptions.RequestEntityTooLarge` 异常。
这个特性是在 Flask 0.6 中被加入的,但是更老的版本也可以通过构建请求对象
的子类来实现。更多信息请查询 Werkzeug 文档中文件处理部分的内容。
上传进度条
--------------------
以前,很多开发者实现进度条的方法是这样的: 一边小块小块地读取传输来的文件,
一边将上传进度储存在数据库中,然后在通过客户端的 JavaScript 代码读取进度。
简单来说,客户端会每5秒钟询问服务器传输的进度。您感觉到这种讽刺了么?客户端
询问一些他本应该已经知道的事情。
现在有了一些性能更好、运行更可靠的解决方案。WEB 已经有了不少变化,现在您可以
使用 HTML5、Java、Silverlight 或者 Flash 来实现客户端更好的上传体验。看一看
下面列出的库的连接,可以找到一些很好的样例。
- `Plupload `_ - HTML5, Java, Flash
- `SWFUpload `_ - Flash
- `JumpLoader `_ - Java
更简单解决方案
------------------
因为存在一个处理上传文件的范式,这个范式在大多数应用中机会不会有太大改变,
所以 Flask 存在一个扩展名为 `Flask-Uploads`_ ,这个扩展实现了一整套成熟的
文件上传架构。它提供了包括文件类型白名单、黑名单等多种功能。
.. _Flask-Uploads: http://packages.python.org/Flask-Uploads/