### 导航
- [索引](# "总目录")
- [下一页](# "缓存") |
- [上一页](# "在 Flask 中使用 SQLAlchemy") |
- [Flask 0.10.1 文档](#) »
- [Flask 代码模式](#) »
# 上传文件
哦,上传文件可是个经典的好问题了。文件上传的基本概念实际上非常简单,他基本是这样工作的:
1. 一个 <form> 标签被标记有 enctype=multipart/form-data ,并且在里面包含一个 <inputtype=file> 标签。
1. 服务端应用通过请求对象上的 files 字典访问文件。
1. 使用文件的 [save()](http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.save "(在 Werkzeug v0.10)") [http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.save] 方法将文件永久地保存在文件系统上的某处。
### 一点点介绍
让我们建立一个非常基础的小应用,这个小应用可以上传文件到一个指定的文件夹里,然后将这个文件显示给用户。让我们看看这个应用的基础代码:
~~~
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
~~~
首先我们导入一些东西,大多数内容都是直接而容易的。werkzeug.secure_filename()将会在稍后进行解释。 UPLOAD_FOLDER 是我们储存上传的文件的地方,而 ALLOWED_EXTENSIONS则是允许的文件类型的集合。然后我们手动为应用添加一个的 URL 规则。我们通常很少这样做,但是为什么这里要如此呢?原因是我们希望实际部署的服务器(或者我们的开发服务器)来为我们提供这些文件的访问服务,所以我们只需要一个规则用来生成指向这些文件的 URL 。
为什么我们限制上传文件的后缀呢?您可能不希望您的用户能够上传任何文件到服务器上,如果服务器直接将数据发送给客户端。以这种方式,您可以确保您的用户不能上传可能导致 XSS 问题(参考 [*跨站脚本攻击(XSS)*](#) )的 HTML 文件。也确保会阻止 .php 文件以防其会被运行。当然,谁还会在服务器上安装PHP 啊,是不是? :)
下一步,就是检查文件类型是否有效、上传通过检查的文件、以及将用户重定向到已经上传好的文件 URL 处的函数了:
~~~
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()](http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename "(在 Werkzeug v0.10)") [http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename] 函数具体做了那些事呢?现在的问题是,有一个信条叫做“永远别相信你用户的输入” ,这句话对于上传文件的文件名也是同样有效的。所有提交的表单数据都可以伪造,而文件名本身也可能是危险的。在摄氏只需记住:在将文件保存在文件系统之前,要坚持使用这个函数来确保文件名是安全的。
关于文件名安全的更多信息
您对 [secure_filename()](http://werkzeug.pocoo.org/docs/utils/#werkzeug.utils.secure_filename "(在 Werkzeug v0.10)") [http://werkzeug.pocoo.org/docs/utils/#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/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
~~~
或者,您也可以选择为 uploaded_file 注册 build_only 规则,然后使用[SharedDataMiddleware](http://werkzeug.pocoo.org/docs/middlewares/#werkzeug.wsgi.SharedDataMiddleware "(在 Werkzeug v0.10)") [http://werkzeug.pocoo.org/docs/middlewares/#werkzeug.wsgi.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']
})
~~~
运行应用,不出意外的话,一切都应该像预期那样工作了。
### 改进上传功能
0.6 新版功能.
Flask 到底是如何处理上传的呢?如果服务器相对较小,那么他会先将文件储存在网页服务器的内存当中。否则就将其写入一个临时未知(如函数 [tempfile.gettempdir()](http://docs.python.org/dev/library/tempfile.html#tempfile.gettempdir "(在 Python v3.5)") [http://docs.python.org/dev/library/tempfile.html#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](http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.RequestEntityTooLarge "(在 Werkzeug v0.10)") [http://werkzeug.pocoo.org/docs/exceptions/#werkzeug.exceptions.RequestEntityTooLarge] 异常。
这个特性是在 Flask 0.6 中被加入的,但是更老的版本也可以通过构建请求对象的子类来实现。更多信息请查询 Werkzeug 文档中文件处理部分的内容。
### 上传进度条
以前,很多开发者实现进度条的方法是这样的: 一边小块小块地读取传输来的文件,一边将上传进度储存在数据库中,然后在通过客户端的 JavaScript 代码读取进度。简单来说,客户端会每5秒钟询问服务器传输的进度。您感觉到这种讽刺了么?客户端询问一些他本应该已经知道的事情。
现在有了一些性能更好、运行更可靠的解决方案。WEB 已经有了不少变化,现在您可以使用 HTML5、Java、Silverlight 或者 Flash 来实现客户端更好的上传体验。看一看下面列出的库的连接,可以找到一些很好的样例。
- [Plupload](http://www.plupload.com/) [http://www.plupload.com/] - HTML5, Java, Flash
- [SWFUpload](http://www.swfupload.org/) [http://www.swfupload.org/] - Flash
- [JumpLoader](http://jumploader.com/) [http://jumploader.com/] - Java
### 更简单解决方案
因为存在一个处理上传文件的范式,这个范式在大多数应用中机会不会有太大改变,所以 Flask 存在一个扩展名为 [Flask-Uploads](http://packages.python.org/Flask-Uploads/) [http://packages.python.org/Flask-Uploads/] ,这个扩展实现了一整套成熟的文件上传架构。它提供了包括文件类型白名单、黑名单等多种功能。
© 版权所有 2013, Armin Ronacher.
- 欢迎使用 Flask
- 前言
- 给有经验程序员的前言
- 安装
- 快速入门
- 教程
- 介绍 Flaskr
- 步骤 0: 创建文件夹
- 步骤 1: 数据库模式
- 步骤 2: 应用设置代码
- 步骤 3: 创建数据库
- 步骤 4: 请求数据库连接
- 步骤 5: 视图函数
- 步骤 6: 模板
- 步骤 7: 添加样式
- 福利: 应用测试
- 模板
- 测试 Flask 应用
- 记录应用错误
- 配置处理
- 信号
- 即插视图
- 应用上下文
- 请求上下文
- 用蓝图实现模块化的应用
- Flask 扩展
- 与 Shell 共舞
- Flask 代码模式
- 大型应用
- 应用程序的工厂函数
- 应用调度
- 使用 URL 处理器
- 部署和分发
- 使用 Fabric 部署
- 在 Flask 中使用 SQLite 3
- 在 Flask 中使用 SQLAlchemy
- 上传文件
- 缓存
- 视图装饰器
- 使用 WTForms 进行表单验证
- 模板继承
- 消息闪现
- 用 jQuery 实现 Ajax
- 自定义错误页面
- 延迟加载视图
- 在 Flask 中使用 MongoKit
- 添加 Favicon
- 数据流
- 延迟请求回调
- 添加 HTTP Method Overrides
- 请求内容校验码
- 基于 Celery 的后台任务
- 部署选择
- mod_wsgi (Apache)
- 独立 WSGI 容器
- uWSGI
- FastCGI
- CGI
- 聚沙成塔
- API
- JSON 支持
- Flask 中的设计决策
- HTML/XHTML 常见问题
- 安全注意事项
- Flask 中的 Unicode
- Flask 扩展开发
- Pocoo 风格指引
- Python 3 支持
- 升级到最新版本
- Flask Changelog
- 许可证
- 术语表