我在为 apiflask 适配 Pydantic 文件上传模型时,遇到了一个问题。
flask 在获取上传的文件参数时,有两种方式:
request.filesrequest.files.getlist
如果使用 request.files :
- 单文件上传时,通过参数名获取到的是单文件
- 多文件上传时,通过参数名只能随机获取到多个文件中的一个文件
如果使用 request.files.getlist :
- 单文件上传时,通过参数名获取到的是一个仅包含被上传的单文件的列表
- 多文件上传时,通过参数名获取到的是一个包含所有被上传文件的乱序列表
也就是说,获取到的参数的类型仅与获取参数的方式相关,而和这个参数在被上传时,是单文件上传还是多文件上传无关。故无法判断客户端的初始意图是上传单文件还是文件列表。在将参数转换为 Pydantic 模型时,也无法将文件参数类型进行精确匹配,因为无法知悉其到底是文件类型还是文件列表类型(如上所述,这仅取决于获取参数的方式,无法判断客户端的初始意图是什么)。
也许还有其他什么我尚不知晓的获取文件参数的方式?或者大家有什么其他的想法或思路可以处理这个问题?
欢迎大家留言讨论,不胜感激。
以下是最小演示示例:
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