File uploads are one of those features that look simple… until they aren’t.
Images need previewing. CSV files need parsing. Excel files need validation. Large files need handling. And suddenly your “simple upload” becomes a full feature.
In this guide, we’ll build a practical and production-ready file upload system using:
- Angular (Frontend)
- Django + Django REST Framework (Backend)
We’ll cover:
- Image uploads with preview
- CSV uploads and parsing
- Excel uploads and processing
- Validation and security best practices
Backend Setup (Django + DRF)
1. Install Dependencies
1
| pip install djangorestframework pillow openpyxl pandas
|
pillow → Image processing
openpyxl → Excel support
pandas → CSV & Excel parsing
settings.py
1
2
| MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
urls.py (project level)
1
2
3
4
5
6
7
8
9
| from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# your urls
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
Part 1: Image Upload
Django Model
1
2
3
4
5
| from django.db import models
class Profile(models.Model):
name = models.CharField(max_length=100)
image = models.ImageField(upload_to='profiles/')
|
Serializer
1
2
3
4
5
6
7
| from rest_framework import serializers
from .models import Profile
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
|
View
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework import status
class ProfileUploadView(APIView):
parser_classes = [MultiPartParser, FormParser]
def post(self, request):
serializer = ProfileSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
Angular Frontend (Image Upload)
HTML
1
2
3
| <input type="file" (change)="onFileSelected($event)" accept="image/*" />
<img *ngIf="previewUrl" [src]="previewUrl" width="200" />
<button (click)="upload()">Upload</button>
|
Component
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| selectedFile!: File;
previewUrl: string | ArrayBuffer | null = null;
onFileSelected(event: any) {
this.selectedFile = event.target.files[0];
const reader = new FileReader();
reader.onload = () => {
this.previewUrl = reader.result;
};
reader.readAsDataURL(this.selectedFile);
}
upload() {
const formData = new FormData();
formData.append('name', 'Billy');
formData.append('image', this.selectedFile);
this.http.post('http://localhost:8000/api/upload/', formData)
.subscribe(res => console.log(res));
}
|
Part 2: CSV Upload and Parsing
Django View for CSV
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| import pandas as pd
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.parsers import MultiPartParser
class CSVUploadView(APIView):
parser_classes = [MultiPartParser]
def post(self, request):
file = request.FILES['file']
if not file.name.endswith('.csv'):
return Response({'error': 'Invalid file type'}, status=400)
df = pd.read_csv(file)
# Example processing
total_rows = len(df)
columns = list(df.columns)
return Response({
'rows': total_rows,
'columns': columns
})
|
Angular CSV Upload
1
2
3
4
5
6
7
| uploadCSV(file: File) {
const formData = new FormData();
formData.append('file', file);
this.http.post('http://localhost:8000/api/upload-csv/', formData)
.subscribe(res => console.log(res));
}
|
Part 3: Excel Upload (.xlsx)
Django View for Excel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| class ExcelUploadView(APIView):
parser_classes = [MultiPartParser]
def post(self, request):
file = request.FILES['file']
if not file.name.endswith('.xlsx'):
return Response({'error': 'Invalid file type'}, status=400)
df = pd.read_excel(file)
summary = {
'rows': len(df),
'columns': list(df.columns)
}
return Response(summary)
|
Validation & Security Best Practices
1. Limit File Size
In settings.py:
1
| DATA_UPLOAD_MAX_MEMORY_SIZE = 5242880 # 5MB
|
2. Validate File Type Properly
Don’t rely only on extension.
1
2
| if file.content_type not in ['image/jpeg', 'image/png']:
return Response({'error': 'Invalid image format'}, status=400)
|
3. Rename Uploaded Files
1
2
3
4
5
| import uuid
def upload_to(instance, filename):
ext = filename.split('.')[-1]
return f"uploads/{uuid.uuid4()}.{ext}"
|
4. Handle Large Files with Streaming
For very large CSV files, process in chunks:
1
2
3
| for chunk in pd.read_csv(file, chunksize=1000):
# process chunk
pass
|
Bonus: Returning Processed Data
Sometimes you don’t just upload — you process and return a result file.
Example: Generate processed CSV
1
2
3
4
5
6
| from django.http import HttpResponse
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="processed.csv"'
df.to_csv(response, index=False)
return response
|
Final Thoughts
File uploads are not just about saving files.
They are about:
- Validation
- Security
- User experience
- Scalability
When done correctly, they become powerful data pipelines inside your application.
If you’re building admin systems, reporting tools, learning platforms, or fintech dashboards — mastering uploads is essential.