Skip to content

Commit ab4b31c

Browse files
stackjayjaygaha
authored andcommitted
day #48 fastapi #8 file uploads
1 parent e2cf516 commit ab4b31c

14 files changed

Lines changed: 1903 additions & 0 deletions

File tree

workspace/7_framework/fastapi/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ uvicorn main:app --reload
9393
Learn how to work with middleware to process requests and responses globally and how to configure Cross-Origin Resource Sharing (CORS) to allow your frontend applications to interact with your API.
9494
_Includes: custom middleware, logging, performance monitoring, and CORS configuration._
9595

96+
- [Day 08: Advanced File Uploads and Management](day08/README.md)
97+
Learn how to handle advanced file uploads, serve static files, and build a complete file management system with features like validation, image processing, and searching.
98+
_Includes: single and multiple file uploads, static file serving, validation, image thumbnail generation, and a complete file management API._
99+
96100
---
97101

98102
## Recommended Project Structure
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
# FastAPI Day 08: Advanced File Uploads and Static Files
2+
3+
Welcome to **Day 08** of the FastAPI tutorial! Today, you'll dive deep into handling file uploads, a crucial feature for many web applications. You'll learn how to manage single and multiple file uploads, perform server-side validation, and serve the uploaded files back to the user.
4+
5+
---
6+
7+
## What You'll Learn
8+
9+
- Handle single, multiple, and metadata-rich file uploads.
10+
- Implement robust server-side validation for file size and type.
11+
- Store files on the server with a structured and secure approach.
12+
- Serve uploaded files statically using `StaticFiles`.
13+
- Process image files, including generating thumbnails.
14+
- Stream large files efficiently to reduce memory usage.
15+
- Structure a file-handling application by separating concerns.
16+
17+
---
18+
19+
## Key Concepts
20+
21+
### 1. Handling File Uploads with `UploadFile`
22+
23+
FastAPI makes handling uploaded files straightforward with the `UploadFile` class. Unlike a simple `bytes` object, `UploadFile` is a spooled file, which means it stores the file in memory up to a certain size limit and then spills it over to disk. This makes it efficient for both small and large files.
24+
25+
To use it, you declare a parameter in your path operation function with the `UploadFile` type hint and the `File()` dependency.
26+
27+
Example from `src/main.py`:
28+
```python
29+
# from src/main.py
30+
from fastapi import FastAPI, File, UploadFile
31+
32+
app = FastAPI()
33+
34+
@app.post("/upload/single/")
35+
async def upload_single_file(file: UploadFile = File(...)):
36+
# ... file processing logic ...
37+
return {"filename": file.filename, "content_type": file.content_type}
38+
```
39+
`UploadFile` has several useful attributes and async methods, including `filename`, `content_type`, `file` (the spooled file object), `read()`, and `seek()`.
40+
41+
### 2. Combining Files with Other Form Data
42+
43+
Often, you need to upload a file along with other data, like a description or a boolean flag. To do this, you use `Form()` for the other data fields. FastAPI understands that when `File()` and `Form()` are mixed, the request should be treated as `multipart/form-data`.
44+
45+
Example from `src/main.py`:
46+
```python
47+
# from src/main.py
48+
from typing import Optional
49+
from fastapi import Form
50+
51+
@app.post("/upload/image/")
52+
async def upload_image(
53+
image: UploadFile = File(...),
54+
create_thumbnail: bool = Form(False),
55+
alt_text: Optional[str] = Form(None)
56+
):
57+
# ... logic for handling the image and other form data ...
58+
return {"alt_text": alt_text, "thumbnail_created": create_thumbnail}
59+
```
60+
61+
### 3. Server-Side Validation
62+
63+
Never trust user-provided files. It's critical to validate files on the server to ensure they meet your application's requirements for size and type. This prevents users from uploading excessively large files that could overwhelm your server or malicious files disguised with a harmless extension.
64+
65+
In our project, the `FileValidator` class in `file_handlers.py` handles this logic.
66+
67+
Example from `src/file_handlers.py`:
68+
```python
69+
# from src/file_handlers.py
70+
from fastapi import UploadFile, HTTPException, status
71+
72+
class FileValidator:
73+
ALLOWED_IMAGE_TYPES = {"image/jpeg", "image/png", "image/gif"}
74+
MAX_IMAGE_SIZE = 5 * 1024 * 1024 # 5MB
75+
76+
@classmethod
77+
async def validate_file(cls, file: UploadFile, file_type: str = "any"):
78+
if file.size > cls.MAX_IMAGE_SIZE:
79+
raise HTTPException(
80+
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
81+
detail="File is too large."
82+
)
83+
if file_type == "image" and file.content_type not in cls.ALLOWED_IMAGE_TYPES:
84+
raise HTTPException(
85+
status_code=status.HTTP_400_BAD_REQUEST,
86+
detail="Invalid image type."
87+
)
88+
return True
89+
```
90+
91+
### 4. File Storage and Organization
92+
93+
Storing files with their original filenames can lead to conflicts if two users upload a file with the same name. A common strategy is to generate a unique filename (e.g., using `uuid.uuid4()`) and store the original filename in a database for later retrieval.
94+
95+
This project also organizes files into category-based subdirectories (`images/`, `documents/`, etc.) within a main `uploads` directory. This separation makes the file system easier to manage.
96+
97+
### 5. Serving Static Files with `StaticFiles`
98+
99+
Once a file is uploaded, you need a way to serve it back to users. FastAPI uses `StaticFiles` to mount a directory, making all of its contents available at a specified URL path.
100+
101+
Example from `src/main.py`:
102+
```python
103+
# from src/main.py
104+
from fastapi.staticfiles import StaticFiles
105+
106+
app.mount("/static", StaticFiles(directory="uploads"), name="static")
107+
```
108+
With this configuration, a file saved at `uploads/images/my-file.png` can be accessed by clients at the URL `http://localhost:8000/static/images/my-file.png`.
109+
110+
### 6. Application Structure
111+
112+
This project separates concerns into different modules, which is a best practice for building maintainable applications:
113+
- `main.py`: Contains the FastAPI app instance and all API endpoint definitions. It handles the "what" (the routes).
114+
- `file_handlers.py`: Contains the business logic for file validation, storage, and processing. It handles the "how" (the implementation details).
115+
- `models.py`: Defines the Pydantic models used for request validation and response serialization. It defines the data shapes.
116+
- `tests/`: Contains unit and integration tests to ensure the application works correctly.
117+
118+
---
119+
120+
## Next Steps
121+
122+
- Explore the different upload endpoints in `src/main.py` (`/upload/single/`, `/upload/image/`, `/upload/multiple/`).
123+
- Review the logic in `FileStorage` and `ImageProcessor` in `src/file_handlers.py` to understand how files are saved and manipulated.
124+
- Run the application and use an API client like Postman or Insomnia to test the file upload endpoints. Try uploading valid and invalid files to see the validation in action.
125+
- After uploading a file, try accessing its `file_url` in your browser to see `StaticFiles` at work.
126+
- Examine the tests in `tests/test_main.py` to see how file uploads are tested programmatically.
127+
128+
---
129+
130+
**Tip:** For production applications, consider using a cloud storage service like Amazon S3, Google Cloud Storage, or Azure Blob Storage instead of the local file system. This provides better scalability, durability, and security.
131+
132+
---
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
asyncio_default_fixture_loop_scope = function
3+
pythonpath = . src
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
fastapi>=0.115.0
2+
pydantic>=1.10.0
3+
pyjwt>=2.4.0
4+
uvicorn>=0.30.0
5+
pillow>=11.3.0
6+
python-multipart>=0.0.20
7+
pytest>=7.0.0
8+
pytest-asyncio>=0.20.0
9+
pytest-cov>=4.0.0
10+
httpx>=0.24.0
11+
flake8>=5.0.0

0 commit comments

Comments
 (0)