Middle
from fastapi import FastAPI, UploadFile, Response from fastapi.responses import StreamingResponse from pydantic import BaseModel, field_validator import base64 app = FastAPI() class PdfSchema(BaseModel): filename: str content_type: str data: str # base64 pdf 10мб @app.get("/data", response_model=PdfSchema) async def get_data(): return await service.get_big_data() @app.get("/download") async def download(): with open("big.pdf", "rb") as f: data = f.read() return Response(content=data, media_type="application/pdf") def heavy_pdf_parse(data: bytes) -> dict: # CPU-heavy логика return {"size": len(data)} @app.post("/parse") async def parse(file: UploadFile): content = await file.read() result = heavy_pdf_parse(content) return result @app.get("/users") async def get_users(): users = await repo.get_users() result = [] for u in users: posts = await repo.get_posts(u["id"]) result.append({"user": u, "posts": posts}) return result @app.get("/download-two") async def download_two(): def iterfile(): with open("big.pdf", "rb") as f: yield from f return StreamingResponse(iterfile(), media_type="application/pdf") class service: @staticmethod async def get_big_data(): return { "filename": "file.pdf", "content_type": "application/pdf", "data": base64.b64encode(b"fake").decode(), } class repo: @staticmethod async def get_users(): return [{"id": 1}, {"id": 2}] @staticmethod async def get_posts(user_id: int): return [{"id": 1, "user id": user_id}]
sobes.tech AI
Answer from AI
The provided code has several important points worth discussing:
-
Passing large PDFs in base64 within JSON (
PdfSchema):- Encodes the PDF in base64 and transmits it as a string. This is inefficient for large files (10 MB and above), as base64 increases the size by about 33%.
- It's better to serve files directly via
StreamingResponse, as in/downloadand/download-two.
-
Serving files via
ResponseandStreamingResponse:- In
/download, the file is read entirely into memory, which is bad for large files. - In
/download-two, a generatoriterfile()is used, which reads the file in parts and streams it — this is more optimal.
- In
-
CPU-heavy function
heavy_pdf_parse:- If parsing takes a lot of CPU, consider moving this logic to a separate worker or using asynchronous queues to avoid blocking the event loop.
-
Asynchronous requests in
/users:- In the loop, for each user,
await repo.get_posts(u["id"])is called, which is sequential and can be slow. - It's better to run all requests in parallel using
asyncio.gatherto speed up:
- In the loop, for each user,
import asyncio
@app.get("/users")
async def get_users():
users = await repo.get_users()
async def get_user_posts(u):
posts = await repo.get_posts(u["id"])
return {"user": u, "posts": posts}
result = await asyncio.gather(*(get_user_posts(u) for u in users))
return result
- Input data validation:
- The code lacks validation of incoming data; for example, for
/parse, a Pydantic model or file type check could be added.
- The code lacks validation of incoming data; for example, for
Overall, the code demonstrates basic techniques for working with FastAPI, but for production, it’s advisable to improve handling of large files, request parallelism, and input validation.