How to Handle File Uploads¶
Warning
The current page still doesn't have a translation for this language.
But you can help translating it: Contributing.
This guide demonstrates how to accept and process file uploads in your Ravyn application.
Problem Statement¶
Web applications often need to receive files from users, such as profile pictures, documents, or CSV files. Handling these multi-part form data requests requires specific parsing and a way to access the file content and metadata safely.
Solution¶
Ravyn uses the UploadFile class to represent uploaded files. You can use the File parameter from ravyn.params to define file upload fields in your handlers.
1. Simple file upload¶
Define a handler that accepts a single file.
from ravyn import Ravyn, post, File, UploadFile
@post("/upload")
async def upload_file(data: UploadFile = File()) -> dict:
# Access file metadata
filename = data.filename
content_type = data.content_type
# Read file content
content = await data.read()
return {
"filename": filename,
"content_type": content_type,
"size": len(content)
}
app = Ravyn(routes=[upload_file])
2. Uploading multiple files¶
You can accept a list of files by typing the parameter as list[UploadFile].
from ravyn import Ravyn, post, File, UploadFile
@post("/upload-multiple")
async def upload_multiple(files: list[UploadFile] = File()) -> dict:
uploaded_files = []
for file in files:
uploaded_files.append({
"filename": file.filename,
"size": len(await file.read())
})
return {"uploaded": uploaded_files}
app = Ravyn(routes=[upload_multiple])
3. Saving an uploaded file¶
Use standard Python file operations to save the uploaded content to the filesystem.
import aiofiles
from ravyn import Ravyn, post, File, UploadFile
@post("/save-avatar")
async def save_avatar(avatar: UploadFile = File()) -> dict:
destination = f"uploads/{avatar.filename}"
async with aiofiles.open(destination, "wb") as f:
content = await avatar.read()
await f.write(content)
return {"message": f"Avatar saved to {destination}"}
app = Ravyn(routes=[save_avatar])
Explanation¶
File(): This is a parameter marker that tells Ravyn to look for the value in the multi-part form data of the request.UploadFile: This object provides an interface to the uploaded file without loading the entire file into memory immediately. It's useful for large files.await file.read(): Reads the entire content of the file. For very large files, you might want to use a loop withawait file.read(chunk_size).await file.seek(0): Moves the file pointer back to the beginning. Useful if you need to read the file multiple times.
Common Pitfalls¶
- Memory usage: Reading a very large file with
await file.read()loads the whole content into RAM. For large files, stream the content in chunks. - Async file I/O: When saving files, use an async library like
aiofilesto avoid blocking the main event loop. - File pointer: After reading a file, the pointer is at the end. If you need to read it again (e.g., for validation then for saving), call
await file.seek(0). - Missing
multipart/form-data: Ensure your client sends the request with the correctContent-Type. Ravyn'sFile()parameter handles this automatically on the server side, but the client must provide it.