Skip to content

Forms and uploaded files

Use await request.form() to preserve repeated form fields and uploaded files. The resulting FormData can come from URL encoded or multipart input. get(key) selects the last value; getAll(key) retains all values. fields groups text and files groups UploadedFile objects. multiItems() preserves the complete sequence of pairs.

Inspecting an attachment without retaining temporary files

Section titled “Inspecting an attachment without retaining temporary files”
from orionis.http import Request, response
async def inspect_attachment(request: Request):
form = await request.form()
try:
uploads = form.files.get("attachment", [])
if len(uploads) != 1:
return response.json({"message": "One attachment is required"}, 422)
upload = uploads[0]
return response.json({
"filename": upload.filename,
"size": upload.size,
"extension": upload.extension,
"labels": form.getAll("label"),
})
finally:
form.close()

The client must send multipart/form-data with a valid boundary. This example stores no data and is useful for checking form integration. FormData also supports a synchronous context manager: with await request.form() as form:. Do not use async with; close() is synchronous.

For a local demonstration, place the handler above in routes/api.py and register Route.post("/attachments/inspect", inspect_attachment) after importing Route from orionis.support.facades. Submit a file field named attachment and repeated text fields named label. The JSON response reports the file name, byte size, extension and all labels; it does not upload the file to persistent storage. Add authentication and authorization before using the endpoint with private files.

UploadedFile.read() returns bytes, chunks(size) iterates blocks and save(path) copies to a destination. These methods are synchronous. Avoid writing a large file to slow storage on the event loop without delegating the work. replace(data) replaces content; size and extension are properties. The extension includes its dot and is lowercase.

The incoming name is sanitized to remove separators, controls and leading dots. This does not inspect content or ensure uniqueness. Generate your own storage name, validate bytes and MIME according to your domain, and use an authorized destination. Do not turn a client name directly into a public path. For persistent storage, see files.

Files start in memory and spill to a temporary file above HTTP_UPLOAD_MEMORY_THRESHOLD. Retain only persisted results; a closed UploadedFile is no longer a usable source for background work. Persist first and pass a domain identifier or controlled path when processing later.

config/http.py declares file (HTTP_MAX_FILES), field (HTTP_MAX_FIELDS), part (HTTP_MAX_PART_SIZE), field-size (HTTP_MAX_FIELD_SIZE), header-size (HTTP_MAX_PART_HEADER_SIZE), file-spill and total multipart memory (HTTP_MAX_MULTIPART_MEMORY_SIZE) limits. They combine with the overall body limit. Raising the total does not remove individual part limits.

Incomplete uploads, invalid boundaries, excessive parts and oversized content produce parsing or limit errors. The parser cleans temporary uploads on failure; handlers must still avoid retaining references or abandoning their own copies. Size temporary storage and memory per worker multiplied by concurrency.

Initial HTTPBodyLimits values are:

LimitValue
Files / fields32 / 128
Individual part10 MiB
Text field1 MiB
Part header16 KiB
In-memory file threshold256 KiB
Total retained multipart memory8 MiB

Memory budgets exclude some objects, temporary copies and server buffers. Zero files or fields rejects that part kind; it does not mean unlimited.