Flask如何区分上传的是单文件还是多文件?

我在为 apiflask 适配 Pydantic 文件上传模型时,遇到了一个问题。

flask 在获取上传的文件参数时,有两种方式:

  • request.files
  • request.files.getlist

如果使用 request.files

  • 单文件上传时,通过参数名获取到的是单文件
  • 多文件上传时,通过参数名只能随机获取到多个文件中的一个文件

如果使用 request.files.getlist

  • 单文件上传时,通过参数名获取到的是一个仅包含被上传的单文件的列表
  • 多文件上传时,通过参数名获取到的是一个包含所有被上传文件的乱序列表

也就是说,获取到的参数的类型仅与获取参数的方式相关,而和这个参数在被上传时,是单文件上传还是多文件上传无关。故无法判断客户端的初始意图是上传单文件还是文件列表。在将参数转换为 Pydantic 模型时,也无法将文件参数类型进行精确匹配,因为无法知悉其到底是文件类型还是文件列表类型(如上所述,这仅取决于获取参数的方式,无法判断客户端的初始意图是什么)。

也许还有其他什么我尚不知晓的获取文件参数的方式?或者大家有什么其他的想法或思路可以处理这个问题?
欢迎大家留言讨论,不胜感激。

cc @greyli @Farmer-Chillax

以下是最小演示示例:
app.py

from flask import Flask, request
from werkzeug.datastructures import FileStorage


app = Flask(__name__)


@app.route('/upload', methods=['POST'])
def upload_file():
    # 单文件
    assert isinstance(request.files['single_file'], FileStorage)
    assert request.files['single_file'].filename == 'test.txt'

    # 使用 request.files.getlist 即使是单文件也会返回一个 list
    assert isinstance(request.files.getlist('single_file'), list)
    assert len(request.files.getlist('single_file')) == 1
    # list 中只有一个元素,就是这个单文件
    assert request.files.getlist('single_file')[0].filename == 'test.txt'


    # 多文件
    assert isinstance(request.files.getlist('multi_files'), list)
    assert len(request.files.getlist('multi_files')) == 3

    # 使用 request.files 会获取到列表的首个文件
    assert isinstance(request.files['multi_files'], FileStorage)
    # 以下这个断言会概率性失败,因为 list 中文件的顺序和上传顺序并不一定一致
    # assert request.files.getlist('multi_files')[0].filename == 'test1.txt'
    # 但通过 request.files 获取的一定是 list 的首个文件
    assert request.files['multi_files'].filename == request.files.getlist('multi_files')[0].filename



    return {"message": "Passed"}

requests客户端:

import requests


rv = requests.post(
    "http://localhost:5000/upload",
    files={
        ("single_file", ("test.txt", open("test.txt", "rb"), "text/plain")),
        ("multi_files", ("test1.txt", open("test1.txt", "rb"), "text/plain")),
        ("multi_files", ("test2.txt", open("test2.txt", "rb"), "text/plain")),
        ("multi_files", ("test3.txt", open("test3.txt", "rb"), "text/plain")),
    },
)

版本信息:
flask: 3.1.2
requests: 2.32.5

换了一个方案:根据定义的 Pydantic model 字段是单文件类型还是文件列表类型来分别使用request.files.getrequest.files.getlist 来获取文件参数。

欢迎大家 CR :

1 个赞