File Uploads Guide
Panduan upload file dan gambar di aplikasi.
📁 Dua Jenis Upload
| Tipe | API | Use Case | Convert |
|---|---|---|---|
| Image | /api/upload/image | Avatar, photos | WebP |
| File | /api/upload/presign | PDF, ZIP, DOC | - |
🖼️ Image Upload (Avatar)
Cara Kerja
- User pilih file (JPG, PNG, GIF, WebP)
- Server convert ke WebP
- Resize jika avatar (256x256)
- Upload ke R2
- Return public URL
Upload Avatar
Di Profile page:
- Klik icon kamera di avatar
- Pilih gambar dari komputer
- Preview muncul
- Klik "Upload Avatar"
- Done! ✅
Via API
typescript
const formData = new FormData();
formData.append('file', imageFile);
formData.append('type', 'avatar'); // atau 'general'
const res = await fetch('/api/upload/image', {
method: 'POST',
body: formData
});
const { url, size } = await res.json();
// url: https://pub-xxx.r2.dev/avatars/user-id/timestamp.webpImage Processing
Auto-convert ke WebP:
- Ukuran lebih kecil (30-50%)
- Kualitas tetap baik
- Browser support modern
Avatar (type='avatar'):
- Square crop (center)
- Resize ke 256x256
- Quality: 90%
General Image (type='general'):
- Max 1920x1920
- Quality: 85%
- Maintain aspect ratio
Validasi
- ✅ Max 5MB
- ✅ Format: JPG, PNG, GIF, WebP
- ❌ Tidak accept: SVG, BMP, TIFF
📄 File Upload (PDF, ZIP, etc)
Cara Kerja (Presigned URL)
- Minta presigned URL dari server
- Upload langsung ke R2 dari browser
- Server receive confirmation
Via API
Step 1: Get Presigned URL
typescript
const res = await fetch('/api/upload/presign', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filename: 'document.pdf',
contentType: 'application/pdf',
prefix: 'documents' // folder di R2
})
});
const { uploadUrl, publicUrl } = await res.json();Step 2: Upload ke R2
typescript
await fetch(uploadUrl, {
method: 'PUT',
body: file,
headers: { 'Content-Type': 'application/pdf' }
});
// File sekarang di R2!Allowed File Types
| Type | MIME Type |
|---|---|
application/pdf | |
| ZIP | application/zip |
| JSON | application/json |
| TXT | text/plain |
| CSV | text/csv |
| Excel | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| Word | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
🗂️ Struktur Folder di R2
bucket/
├── avatars/
│ └── {user-id}/
│ └── timestamp.webp
├── uploads/
│ └── {user-id}/
│ └── document.pdf
└── images/
└── {user-id}/
└── photo.webp🔒 Security
Access Control
- Avatar: Public URL (bisa diakses semua)
- File upload: Bisa di-restrict dengan presigned URL
- Delete: Hanya owner bisa delete file-nya sendiri
File Validation
- Type checking via MIME type
- Extension whitelist
- Size limit
- Malware scan (via R2)
🐛 Troubleshooting Upload
| Masalah | Solusi |
|---|---|
| "File too large" | Compress file atau resize image |
| "Invalid file type" | Check allowed types di atas |
| "Upload failed" | Check R2 credentials di .env |
| "Image not showing" | Check R2_PUBLIC_URL benar |
| "403 Forbidden" | Bucket public access belum enable |
💡 Best Practices
Untuk Developer
- Always validate file type dan size
- Use unique filenames (timestamp + random)
- Organize by user atau folder
- Delete old files saat update avatar
Untuk User
- Compress images sebelum upload (faster)
- Use WebP jika bisa (smaller size)
- Avatar: Gunakan square image untuk hasil terbaik
📁 Files Terkait
src/
├── lib/
│ ├── image/
│ │ └── convert.ts # WebP conversion
│ └── storage/
│ └── r2.ts # R2 helpers
└── routes/
└── api/
└── upload/
├── image/+server.ts # Image upload
└── presign/+server.ts # Presigned URL