You can use middleware. I used this one and it works nice
Usage (I change the original code a bit):
middleware.py:
from typing import Optional
from fastapi import HTTPException
class ContentSizeLimitMiddleware:
"""Content size limiting middleware for ASGI applications
Args:
app (ASGI application): ASGI application
max_content_size (optional): the maximum content size allowed in bytes, None for no limit
"""
def __init__(
self,
app,
max_content_size: Optional[int] = None,
):
self.app = app
self.max_content_size = max_content_size
def receive_wrapper(self, receive):
received = 0
async def inner():
nonlocal received
message = await receive()
if message["type"] != "http.request" or self.max_content_size is None:
return message
body_len = len(message.get("body", b""))
received += body_len
if received > self.max_content_size:
raise HTTPException(detail="File is too big", status_code=413)
return message
return inner
async def __call__(self, scope, receive, send) -> None:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
wrapper = self.receive_wrapper(receive)
await self.app(scope, wrapper, send)
main.py:
app.add_middleware(ContentSizeLimitMiddleware, max_content_size=5 * 1024 * 1024) # 5MB
This solution reads only the necessary part of the file and reject the request
Take this Starlette view for example:
@app.route("/documents/upload", methods=["POST"])
def upload_document(request):
data = await request.body()
if len(data) > Config.MAX_FILE_SIZE:
return api_400(
f"This file exceeds the maximum file size we support at this time ({Config.MAX_FILE_SIZE})",
code=MAX_FILE_SIZE_EXCEEDED,
)
...
If the maximum file size is 5MB, and the uploaded file was 50MB, then this implementation reads the entire 50MB into memory before rejecting the request.