Django + Flutter Dosya Upload Sistemi (JWT Güvenlikli)
Bu rehber sonunda:
📡 Django API (backend)
📱 Flutter mobil uygulama (frontend)
🔐 JWT Authentication
📂 Dosya yükleme (image/file)
tam çalışan bir mimari kurmuş olacaksın.
GENEL MİMARİ
Flutter App
↓ (HTTP + JWT)
Django REST API
↓
Database + Media Files
⚙️ 1. DJANGO BACKEND KURULUMU
📦 Paketler
pip install djangorestframework
pip install djangorestframework-simplejwt
pip install pillow
🔧 settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
}
🧱 Model
from django.db import models
class Upload(models.Model):
title = models.CharField(max_length=100)
file = models.FileField(upload_to='uploads/')
created_at = models.DateTimeField(auto_now_add=True)
🔌 Serializer
from rest_framework import serializers
from .models import Upload
class UploadSerializer(serializers.ModelSerializer):
class Meta:
model = Upload
fields = '__all__'
🔐 JWT Login
from rest_framework_simplejwt.views import TokenObtainPairView
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view()),
]
📤 Upload API View
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class FileUploadView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
file = request.FILES.get('file')
title = request.data.get('title')
upload = Upload.objects.create(
title=title,
file=file
)
return Response({
"message": "Yüklendi",
"file_url": upload.file.url
})
🌐 urls.py
from django.urls import path
from .views import FileUploadView
urlpatterns = [
path('upload/', FileUploadView.as_view()),
]
📱 2. FLUTTER TARAFI
📦 Paket
dependencies:
http: ^0.13.6
file_picker: ^6.1.1
📂 Dosya Seçme
import 'package:file_picker/file_picker.dart';
Future pickFile() async {
final result = await FilePicker.platform.pickFiles();
if (result != null) {
return result.files.single.path;
}
}
🔐 JWT Login (Token alma)
Future<String> login() async {
final response = await http.post(
Uri.parse("http://127.0.0.1:8000/api/token/"),
body: {
"username": "admin",
"password": "1234"
},
);
return jsonDecode(response.body)['access'];
}
📤 File Upload
import 'dart:io';
import 'package:http/http.dart' as http;
Future uploadFile(String path, String token) async {
var request = http.MultipartRequest(
'POST',
Uri.parse("http://127.0.0.1:8000/upload/"),
);
request.headers['Authorization'] = 'Bearer $token';
request.files.add(
await http.MultipartFile.fromPath('file', path),
);
request.fields['title'] = "Flutter Upload";
var response = await request.send();
if (response.statusCode == 200) {
print("Upload başarılı");
} else {
print("Hata");
}
}
🧪 TEST AKIŞI
Django server çalıştır:
python manage.py runserver
Flutter’da:
Login ol → token al
Dosya seç
Upload yap
🖼️ BONUS – Flutter UI (Basit)
ElevatedButton(
onPressed: () async {
String path = await pickFile();
String token = await login();
await uploadFile(path, token);
},
child: Text("Dosya Yükle"),
)
🔥 PRO SEVİYE GELİŞTİRME
🚀 Ekleyebileceğimiz özellikler:
✅ 📊 Upload progress bar
✅ 🖼️ Resim önizleme
✅ 🗑️ Dosya silme API
✅ 📂 Çoklu dosya upload
✅ ☁️ AWS S3 entegrasyonu
✅ 🔒 Kullanıcıya özel dosyalar
✅ ⚡ Drag & Drop (Flutter Web)
🎯 GERÇEK PROJE FİKRİ
🎓 Okul Yönetim Sistemi
Öğrenci ödev yükler
Öğretmen dosyayı indirir
Admin kontrol eder
💻 1. FULL PROJE (Django + Flutter)
📁 PROJE YAPISI
project-root/
│
├── backend/ (Django API)
│ ├── config/
│ ├── apps/
│ │ └── uploads/
│ ├── media/
│ ├── requirements.txt
│
├── mobile/ (Flutter App)
│ ├── lib/
│ │ ├── screens/
│ │ ├── services/
│ │ ├── widgets/
│
└── README.md
🔧 BACKEND (DJANGO PRO)
📦 requirements.txt
Django
djangorestframework
djangorestframework-simplejwt
Pillow
django-cors-headers
⚙️ settings.py (PRO)
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
CORS_ALLOW_ALL_ORIGINS = True
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
)
}
🧱 Model
class Upload(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
file = models.FileField(upload_to='uploads/')
created_at = models.DateTimeField(auto_now_add=True)
🔌 Serializer
class UploadSerializer(serializers.ModelSerializer):
class Meta:
model = Upload
fields = "__all__"
🔐 Login API
path('api/token/', TokenObtainPairView.as_view()),
📤 Upload API (PRO)
class UploadView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
file = request.FILES.get('file')
upload = Upload.objects.create(
user=request.user,
title=request.data.get('title'),
file=file
)
return Response({
"id": upload.id,
"file": upload.file.url
})
📥 Dosya Listeleme API
class FileListView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
files = Upload.objects.filter(user=request.user)
serializer = UploadSerializer(files, many=True)
return Response(serializer.data)
📱 FLUTTER PRO APP
📦 pubspec.yaml
dependencies:
http: ^0.13.6
file_picker: ^6.1.1
provider: ^6.0.5
🧠 API Service
class ApiService {
String baseUrl = "http://127.0.0.1:8000";
Future<String> login(String user, String pass) async {
final res = await http.post(
Uri.parse("$baseUrl/api/token/"),
body: {"username": user, "password": pass},
);
return jsonDecode(res.body)['access'];
}
}
📤 Upload Service
Future uploadFile(String path, String token) async {
var req = http.MultipartRequest(
"POST",
Uri.parse("http://127.0.0.1:8000/upload/")
);
req.headers['Authorization'] = 'Bearer $token';
req.files.add(await http.MultipartFile.fromPath('file', path));
await req.send();
}
📂 File List Screen
Future getFiles(String token) async {
final res = await http.get(
Uri.parse("http://127.0.0.1:8000/files/"),
headers: {"Authorization": "Bearer $token"},
);
return jsonDecode(res.body);
}
🖼️ UI (Basit ama gerçek)
Column(
children: [
ElevatedButton(
onPressed: pickAndUpload,
child: Text("Dosya Yükle"),
),
Expanded(
child: ListView.builder(
itemCount: files.length,
itemBuilder: (_, i) => ListTile(
title: Text(files[i]['title']),
),
),
)
],
)
🎨 2. FİGMA UI TASARIM (GERÇEK APP)
Sana profesyonel mobil uygulama ekranlarını anlatıyorum 👇
📱 1. Login Screen
Logo üstte
Email / Şifre input
"Giriş Yap" butonu
Gradient arka plan
📱 2. Home Screen
AppBar: “Dosyalarım”
FloatingActionButton (+)
Liste halinde dosyalar
📱 3. Upload Screen
Dosya seç butonu
Dosya adı preview
Upload butonu
Progress bar
📱 4. File Detail Screen
Dosya adı
Tarih
“İndir” butonu
“Sil” butonu
🎯 Figma Design Özellikleri
🎨 Renk Paleti:
Primary: #4A90E2
Secondary: #50E3C2
Background: #F5F7FA
🔤 Font:
Google Fonts → Poppins
📐 UI Stil:
Modern
Rounded corners
Soft shadow
🚀 PRO DJANGO + FLUTTER UPLOAD SİSTEMİ
🔥 Bu versiyonda neler var?
✅ JWT (access + refresh token)
✅ Register / Login sistemi
✅ Kullanıcıya özel dosyalar
✅ Çoklu dosya upload
✅ Dosya silme
✅ Progress bar (Flutter)
✅ Clean Architecture (backend + frontend)
✅ Error handling
✅ API service katmanı
🧠 1. BACKEND (DJANGO PRO ARCHITECTURE)
📁 KLASÖR YAPISI
backend/
├── core/
├── apps/
│ ├── users/
│ └── uploads/
├── services/
├── api/
├── media/
🔐 JWT (Refresh Token Sistemi)
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
urlpatterns = [
path('api/token/', TokenObtainPairView.as_view()),
path('api/token/refresh/', TokenRefreshView.as_view()),
]
👤 USER REGISTER API
class RegisterView(APIView):
def post(self, request):
user = User.objects.create_user(
username=request.data['username'],
password=request.data['password']
)
return Response({"message": "User created"})
📂 UPLOAD SERVICE (Clean Code)
👉 Business logic’i view’dan ayırıyoruz:
# services/upload_service.py
def create_upload(user, file, title):
return Upload.objects.create(
user=user,
file=file,
title=title
)
📤 UPLOAD API (MULTIPLE SUPPORT)
class UploadView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
files = request.FILES.getlist('files')
uploaded_files = []
for f in files:
obj = create_upload(request.user, f, request.data.get('title'))
uploaded_files.append(obj.file.url)
return Response({
"files": uploaded_files
})
📥 DOSYA LİSTELEME
class FileListView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
files = Upload.objects.filter(user=request.user)
return Response([
{
"id": f.id,
"file": f.file.url,
"title": f.title
} for f in files
])
🗑️ DOSYA SİLME
class DeleteFileView(APIView):
permission_classes = [IsAuthenticated]
def delete(self, request, id):
file = Upload.objects.get(id=id, user=request.user)
file.delete()
return Response({"message": "Silindi"})
📱 2. FLUTTER PRO ARCHITECTURE
📁 KLASÖR YAPISI
lib/
├── models/
├── services/
├── providers/
├── screens/
├── widgets/
🧠 AUTH SERVICE
class AuthService {
Future<Map> login(String user, String pass) async {
final res = await http.post(
Uri.parse("$baseUrl/api/token/"),
body: {"username": user, "password": pass},
);
return jsonDecode(res.body);
}
}
🔁 TOKEN REFRESH
Future<String> refreshToken(String refresh) async {
final res = await http.post(
Uri.parse("$baseUrl/api/token/refresh/"),
body: {"refresh": refresh},
);
return jsonDecode(res.body)['access'];
}
📤 MULTI FILE UPLOAD + PROGRESS
Future uploadFiles(List<String> paths, String token) async {
var request = http.MultipartRequest(
'POST',
Uri.parse("$baseUrl/upload/")
);
request.headers['Authorization'] = 'Bearer $token';
for (var path in paths) {
request.files.add(
await http.MultipartFile.fromPath('files', path),
);
}
var response = await request.send();
response.stream.listen((value) {
print("Yükleniyor...");
});
}
📊 PROVIDER (STATE MANAGEMENT)
class FileProvider with ChangeNotifier {
List files = [];
void setFiles(data) {
files = data;
notifyListeners();
}
}
🖼️ HOME SCREEN (REAL UI)
Column(
children: [
ElevatedButton(
onPressed: pickAndUpload,
child: Text("Dosya Yükle"),
),
Expanded(
child: ListView.builder(
itemCount: provider.files.length,
itemBuilder: (_, i) {
var file = provider.files[i];
return ListTile(
title: Text(file['title']),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => deleteFile(file['id']),
),
);
},
),
)
],
)
🎨 PRO UI (Figma seviyesinde)
🔥 Modern UX Özellikleri
Floating Action Button (+ Upload)
Swipe to delete
Loading spinner
Upload progress bar
Image preview
🔒 SECURITY (ÇOK ÖNEMLİ)
✅ Kullanıcı sadece kendi dosyasını görür
✅ JWT zorunlu
✅ Dosya boyut limiti
✅ Dosya tipi kontrolü
☁️ PRODUCTION (GERÇEK DÜNYA)
Bunu eklersen seviye uçar:
Nginx
Gunicorn
AWS S3 (media storage)
Cloudflare CDN
💥 SONUÇ
Bu proje:
🎓 GitHub’a koy → staj garantisi seviyesinde
💼 Freelance → para kazanabileceğin proje
🧠 Öğrenme → gerçek backend + mobil mimari
🔐 JWT güvenlik + 🖱️ Drag & Drop + 📊 Progress + ☁️ AWS S3
Bu rehber = gerçek production mimarisi
🔐 1. JWT ile GÜVENLİ UPLOAD (Django)
🎯 Amaç
Sadece giriş yapan kullanıcı upload yapabilsin
Token süresi dolarsa otomatik yenilensin
⚙️ settings.py (JWT ayarları)
from datetime import timedelta
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
"AUTH_HEADER_TYPES": ("Bearer",),
}
🧠 Güvenli Upload View
from rest_framework.permissions import IsAuthenticated
class SecureUploadView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
file = request.FILES.get('file')
if not file:
return Response({"error": "Dosya yok"}, status=400)
if file.size > 5 * 1024 * 1024:
return Response({"error": "Max 5MB"}, status=400)
obj = Upload.objects.create(
user=request.user,
file=file,
title=request.data.get('title')
)
return Response({
"file": obj.file.url
})
🔒 Flutter → Token ile istek
headers: {
"Authorization": "Bearer $accessToken"
}
🔁 Token otomatik yenileme (ÇOK KRİTİK)
if (response.statusCode == 401) {
accessToken = await refreshToken(refreshToken);
}
🖱️ 2. Drag & Drop Upload UI
📱 Flutter (Web & Desktop destekli)
import 'package:flutter_dropzone/flutter_dropzone.dart';
late DropzoneViewController controller;
DropzoneView(
onCreated: (ctrl) => controller = ctrl,
onDrop: (ev) async {
final bytes = await controller.getFileData(ev);
print("Dosya bırakıldı!");
},
)
🎯 UX
✔ Dosyayı sürükle → bırak
✔ Otomatik upload başlar
✔ Progress gösterilir
📊 3. Progress Bar + AJAX Upload
📡 Flutter (gerçek progress)
var request = http.MultipartRequest(
'POST',
Uri.parse(uploadUrl),
);
var streamedResponse = await request.send();
streamedResponse.stream.listen((value) {
print("yükleniyor...");
});
🧠 Daha profesyonel (yüzdelik progress)
import 'package:dio/dio.dart';
Dio dio = Dio();
await dio.post(
uploadUrl,
data: formData,
onSendProgress: (sent, total) {
double progress = sent / total;
print("Progress: $progress");
},
);
📊 UI
LinearProgressIndicator(value: progress)
☁️ 4. AWS S3 ENTEGRASYONU (PRO LEVEL)
📦 Paket
pip install boto3 django-storages
⚙️ settings.py
INSTALLED_APPS += ['storages']
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
AWS_ACCESS_KEY_ID = 'KEY'
AWS_SECRET_ACCESS_KEY = 'SECRET'
AWS_STORAGE_BUCKET_NAME = 'bucket-name'
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
🎯 Sonuç
Artık dosyalar:
❌ local değil
✅ AWS S3 üzerinde saklanır
📤 Upload sonrası URL
{
"file": "https://bucket.s3.amazonaws.com/uploads/file.jpg"
}
🔥 FULL SİSTEM AKIŞI
Flutter (Drag & Drop + Progress)
↓
JWT Token ile API isteği
↓
Django (Auth kontrol)
↓
AWS S3 (dosya kaydı)
↓
URL geri döner
↓
Flutter UI güncellenir
Bu sistem = şirketlerde kullanılan gerçek mimari.
🚀 ULTIMATE PROJE (Django + Flutter + S3 + Docker)
🔥 Bu projede NELER VAR?
✅ JWT (access + refresh + auto refresh)
✅ Drag & Drop upload (Flutter Web dahil)
✅ Progress bar (gerçek zamanlı)
✅ AWS S3 (cloud storage)
✅ Docker (tek komutla kurulum)
✅ Nginx (production server)
✅ Rate limiting + güvenlik
✅ Clean Architecture (backend + frontend)
✅ Admin dashboard (istatistikli)
1. SİSTEM MİMARİSİ
Flutter App / Web
↓
Nginx (Reverse Proxy)
↓
Django API (Gunicorn)
↓
Redis (Cache + Rate Limit)
↓
PostgreSQL
↓
AWS S3 (Dosyalar)
🐳 2. DOCKER SETUP (TEK KOMUTLA ÇALIŞAN SİSTEM)
📁 docker-compose.yml
version: "3.9"
services:
web:
build: .
command: gunicorn config.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/app
depends_on:
- db
- redis
db:
image: postgres:15
environment:
POSTGRES_DB: app
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
redis:
image: redis:7
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
⚙️ 3. DJANGO PRODUCTION SETTINGS
DEBUG = False
ALLOWED_HOSTS = ["*"]
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "app",
"USER": "user",
"PASSWORD": "pass",
"HOST": "db",
"PORT": 5432,
}
}
🔒 4. RATE LIMIT (GÜVENLİK)
📦 Paket
pip install django-ratelimit
🔐 Kullanım
from django_ratelimit.decorators import ratelimit
@ratelimit(key='ip', rate='5/m', block=True)
def upload_view(request):
...
☁️ 5. AWS S3 (FINAL STORAGE)
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
📊 6. ADMIN DASHBOARD (PRO)
Özellikler:
📈 Günlük upload sayısı
👤 Kullanıcı bazlı istatistik
📂 Dosya türü analizi
🕒 Zaman grafikleri
📱 7. FLUTTER (ULTIMATE UI)
🎨 Ekranlar
🔐 Login Screen
JWT login
Error handling
🏠 Dashboard
Dosya listesi
Arama + filtre
📤 Upload Screen
Drag & Drop
Progress bar
📄 File Detail
Preview (image/pdf)
Download
Delete
📊 PROGRESS BAR (GERÇEK)
onSendProgress: (sent, total) {
setState(() {
progress = sent / total;
});
}
🖱️ DRAG & DROP UX
Dosyayı sürükle → bırak
Otomatik upload
Progress animasyonu
🔁 TOKEN AUTO REFRESH FLOW
Request → 401
↓
Refresh Token
↓
Yeni Access Token
↓
Retry Request
⚡ NGINX CONFIG (ÖZET)
server {
listen 80;
location / {
proxy_pass http://web:8000;
}
}
🎯 DEPLOY (GERÇEK SUNUCU)
🌐 Nerede çalıştırabilirsin?
DigitalOcean
Amazon Web Services
Railway
Yorumlar
Yorum Gönder