上传文件

    1. 一个带有 的 <form> 标记,标记中含有 一个 <input type=file>

    2. 应用通过请求对象的 files 字典来访问文件。

    3. 使用文件的 save() 方法把文件 永久地保存在文件系统中。

    让我们从一个基本的应用开始,这个应用上传文件到一个指定目录,并把文件显示给 用户。以下是应用的前导代码:

    首先我们导入了一堆东西,大多数是浅显易懂的。 werkzeug.secure_filename() 会在稍后解释。 UPLOAD_FOLDER 是上 传文件要储存的目录, ALLOWED_EXTENSIONS 是允许上传的文件扩展名的集 合。

    为什么要限制文件件的扩展名呢?如果直接向客户端发送数据,那么你可能不会 想让用户上传任意文件。否则,你必须确保用户不能上传 HTML 文件,因为 HTML 可能引起 XSS 问题(参见 )。如果服务器可以执行 PHP 文件,那么还必须确保不允许上传 .php 文件。但是谁又会在服务器上安装 PHP 呢,对不? :)

    下一个函数检查扩展名是否合法,上传文件,把用户重定向到已上传文件的 URL:

    1. def allowed_file(filename):
    2. return '.' in filename and \
    3. filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
    4. @app.route('/', methods=['GET', 'POST'])
    5. def upload_file():
    6. # check if the post request has the file part
    7. flash('No file part')
    8. return redirect(request.url)
    9. file = request.files['file']
    10. # If the user does not select a file, the browser submits an
    11. # empty file without a filename.
    12. if file.filename == '':
    13. flash('No selected file')
    14. return redirect(request.url)
    15. if file and allowed_file(file.filename):
    16. filename = secure_filename(file.filename)
    17. file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    18. return '''
    19. <!doctype html>
    20. <title>Upload new File</title>
    21. <form method=post enctype=multipart/form-data>
    22. <input type=file name=file>
    23. <input type=submit value=Upload>
    24. </form>
    25. '''

    进一步说明

    你可以会好奇 secure_filename() 做了哪些工作,如果 不使用它会有什么后果。假设有人把下面的信息作为 filename 传递给你的应 用:

    假设 ../ 的个数是正确的,你会把它和 UPLOAD_FOLDER 结合在一起,那 么用户就可能有能力修改一个服务器上的文件,这个文件本来是用户无权修改的。 这需要了解应用是如何运行的,但是请相信我,黑客都是很变态的 :)

    现在来看看函数是如何工作的:

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

    我们需要为已上传的文件提供服务,使之能够被用户下载。我们将定义一个 download_file 视图来为上传文件夹中的文件提供服务, url_for("download_file", name=name) 依据文件名生成下载 URL 。

    如果您正在使用中间件或者 HTTP 服务器为文件提供服务,那么可以把 download_file 端点注册为 build_only 规则,这样 url_for 会在 没有视图函数的情况下生效。

    1. app.add_url_rule(
    2. "/uploads/<name>", endpoint="download_file", build_only=True

    Changelog

    Flask 到底是如何处理文件上传的呢?如果上传的文件很小,那么会把它们储存 在内存中。否则就会把它们保存到一个临时的位置(通过 可以得到这个位置)。但是,如何限制上传文件 的尺寸呢?缺省情况下, Flask 是不限制上传文件的尺寸的。可以通过设置配置 的 MAX_CONTENT_LENGTH 来限制文件尺寸:

    上面的代码会把尺寸限制为 16 M 。如果上传了大于这个尺寸的文件, Flask 会抛 出一个 RequestEntityTooLarge 异常。

    连接重置问题

    当使用本地开发服务器时,可能会得到一个连接重置,而不是一个 413 响应。 在生产 WSGI 服务器上运行应用时会得到正确的响应。

    Flask 0.6 版本中添加了这个功能。但是通过继承请求对象,在较老的版本中也可以 实现这个功能。更多信息请参阅 Werkzeug 关于文件处理的文档。

    在不久以前,许多开发者是这样实现上传进度条的:分块读取上传的文件,在数 据库中储存上传的进度,然后在客户端通过 JavaScript 获取进度。客户端每 5 秒钟向服务器询问一次上传进度。觉得讽刺吗?客户端在明知故问。

    现在有了更好的解决方案,更快且更可靠。像 之类的 JavaScript 库包 含成的轻松构建进度条的插件。