microdot.multipart --- multipart/form-data 解析

解析 Content-Type: multipart/form-data 请求体——浏览器用于包含 <input type="file"> 字段的表单的编码方式。提供两种类型的 API:

  • 流式的 FormDataIter,每次产出一个字段——当应用程序必须在内存受限的设备上逐块处理超大上传时很有用。

  • with_form_data() 装饰器,它会缓冲所有内容并填充 request.form / request.files——适用于普通大小上传的便捷 API。

class FormDataIter

class microdot.multipart.FormDataIter(request)

request 的 multipart 请求体各部分进行迭代的异步迭代器。产出的值是 (name, value) 元组;对于常规字段,valuestr,对于文件字段,则是 FileUpload

在内存比易用性更重要时直接使用:

from microdot.multipart import FormDataIter, FileUpload

@app.post('/upload')
async def upload(request):
    async for name, value in FormDataIter(request):
        if isinstance(value, FileUpload):
            await value.save('/sdcard/' + value.filename)
        else:
            print(name, '=', value)
    return 'ok'

备注

在迭代文件字段时,必须在下一次 async for 迭代之前消费完该文件(通过 FileUpload.read()FileUpload.save())——迭代推进时底层流会失效。

buffer_size: int

类属性。从请求流中读取时使用的块大小。默认为 256 字节。增大它可提高吞吐量,但代价是占用更多 RAM。

class FileUpload

class microdot.multipart.FileUpload(filename: str, content_type: str | None, read)

单个已上传的文件。实例由 FormDataIter 产出,并由 with_form_data() 收集到 request.files 中。应用程序通常不会直接构造 FileUpload

filename: str

客户端发送时该文件的原始名称(不可信——未经净化处理请勿传给 open())。

content_type: str | None

来自该部分的 Content-Type 标头的 MIME 类型,未提供时为 None

max_memory_size: int

类属性。copy() 从内存缓冲切换到临时文件的阈值(以字节为单位)。默认为 1024。

async read(n: int = -1)

从上传流中读取至多 n 字节。-1 表示读取到末尾。

async save(path_or_file)

将上传保存到 path_or_file,它可以是文件系统路径或一个已打开的文件对象。

async copy(max_memory_size: int | None = None)

缓冲上传内容(视 max_memory_size 而定,存于 RAM 或临时文件中),以便在原始流不失效的情况下解析 multipart 请求体的其余部分。with_form_data() 装饰器会自动调用此方法。

async close()

释放 copy() 创建的任何临时文件。如果上传是通过 with_form_data() 到达 request.files 的,则在请求结束时自动调用。

模块级装饰器

microdot.multipart.with_form_data(f)

在处理程序运行之前预先解析 multipart 请求体并填充 request.formrequest.files 的装饰器:

from microdot import Microdot
from microdot.multipart import with_form_data

app = Microdot()

@app.post('/upload')
@with_form_data
async def upload(request):
    print('fields:', dict(request.form))
    for name, file in request.files.items():
        await file.save('/sdcard/' + sanitize(file.filename))
    return 'ok'

文件上传会通过 FileUpload.copy() 进行缓冲,因此处理程序可以自由地迭代 request.filesrequest.form。请求结束时临时文件会被自动清理。

对于大于几兆字节的上传,建议使用流式的 FormDataIter API;with_form_data() 会在处理程序运行之前将整个请求累积在内存中或文件系统上。