Python之flask文件上传

本文深入讲解了文件上传的工作原理,包括使用multipart/form-data格式、服务器端处理上传文件、安全保存文件及限制上传文件类型等关键步骤。介绍了Flask框架下实现文件上传的基本代码示例,探讨了文件名安全性问题,并提供了上传文件大小限制和上传进度条的实现方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文件上传工作原理

1.一个 <form> 标签被标记有 enctype=multipart/form-data ,并且在里面包含一个 <input type=file> 标签。
2.服务端应用通过请求对象上的 files 字典访问文件。
3.使用文件的 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() 函数具体做了那些事呢?现在的问题是,有一个信条叫做“永远别相信你用户的输入” ,这句话对于上传文件的文件名也是同样有效的。所有提交的表单数据都可以伪造,而文件名本身也可能是危险的。在摄氏只需记住: 在将文件保存在文件系统之前,要坚持使用这个函数来确保文件名是安全的。

关于文件名安全的更多信息

您对 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 类来实现下载服务。这种方法同时支持更老版本的 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']
})

运行应用,不出意外的话,一切都应该像预期那样工作了。
改进上传功能

上传文件大小限制

Flask 到底是如何处理上传的呢?如果服务器相对较小,那么他会先将文件储存在网页服务器的内存当中。否则就将其写入一个临时未知(如函数 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 中被加入的,但是更老的版本也可以通过构建请求对象的子类来实现。更多信息请查询 Werkzeug 文档中文件处理部分的内容。

上传进度条

以前,很多开发者实现进度条的方法是这样的: 一边小块小块地读取传输来的文件, 一边将上传进度储存在数据库中,然后在通过客户端的 JavaScript 代码读取进度。 简单来说,客户端会每5秒钟询问服务器传输的进度。您感觉到这种讽刺了么?客户端询问一些他本应该已经知道的事情。

现在有了一些性能更好、运行更可靠的解决方案。WEB 已经有了不少变化,现在您可以使用 HTML5、Java、Silverlight 或者 Flash 来实现客户端更好的上传体验。看一看下面列出的库的连接,可以找到一些很好的样例。

Plupload - HTML5, Java, Flash
SWFUpload - Flash
JumpLoader - Java

更简单解决方案

因为存在一个处理上传文件的范式,这个范式在大多数应用中机会不会有太大改变, 所以 Flask 存在一个扩展名为 Flask-Uploads ,这个扩展实现了一整套成熟的文件上传架构。它提供了包括文件类型白名单、黑名单等多种功能。

### 使用Python Flask实现文件管理平台 以下是通过Python Flask框架实现一个简单的文件管理平台的方法,该平台支持文件上传和下载功能。 #### 1. 安装依赖库 为了运行此项目,需安装Flask及相关模块。可以通过pip命令完成安装: ```bash pip install flask ``` --- #### 2. 创建基本的Flask应用结构 定义一个Flask应用程序并设置静态目录用于存储上传的文件: ```python import os from flask import Flask, request, jsonify, send_from_directory from werkzeug.utils import secure_filename app = Flask(__name__) # 配置文件保存路径 UPLOAD_FOLDER = 'uploads' if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'} def allowed_file(filename): """检查文件扩展名是否允许""" return '.' in filename and \ filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS ``` 上述代码设置了`UPLOAD_FOLDER`变量指向文件存储位置,并定义了一个辅助函数`allowed_file()`来验证文件类型[^1]。 --- #### 3. 添加文件上传接口 创建一个路由以接收POST请求并将文件保存到指定目录中: ```python @app.route('/upload', methods=['POST']) def upload_file(): if 'file' not in request.files: return jsonify({'error': 'No file part'}), 400 file = request.files['file'] if file.filename == '': return jsonify({'error': 'No selected file'}), 400 if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) return jsonify({'message': f'{filename} uploaded successfully.'}), 200 return jsonify({'error': 'File type not supported'}), 400 ``` 这段代码实现了文件上传逻辑,其中使用了`secure_filename()`方法确保文件名称安全[^5]。 --- #### 4. 添加文件下载接口 创建另一个路由供客户端下载已上传的文件: ```python @app.route('/download/<string:filename>', methods=['GET']) def download_file(filename): uploads = app.config['UPLOAD_FOLDER'] if os.path.exists(os.path.join(uploads, filename)): return send_from_directory(directory=uploads, path=filename, as_attachment=True) else: return jsonify({'error': 'File does not exist'}), 404 ``` 此处利用了Flask内置的`send_from_directory()`方法简化文件传输过程[^3]。 --- #### 5. 启动服务 最后,在脚本底部添加启动配置: ```python if __name__ == '__main__': app.run(debug=True, host='0.0.0.0') ``` 完整的程序能够监听HTTP请求,处理文件上传与下载操作[^4]。 --- ### 示例测试 假设已经部署好以上代码,则可通过以下方式访问API: - **上传文件**: 发送POST请求至 `/upload` 接口,附带名为 `file` 的表单字段。 - **下载文件**: 访问URL形如 `/download/example.txt` 即可获取对应资源。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值