Upload File
Hệ thống cần xử lý upload file tầm 500MB - 1GB, bạn sẽ thiết kế thế nào để đảm bảo tính ổn định ?
==> Giải pháp tốt nhất là Chunked Upload + Pre-signed URL.
🎯 Mục tiêu chính:
Xử lý upload file lớn (500MB - 1GB) một cách ổn định.
Không gây time-out ở phía client/backend.
Không gây overload hệ thống.
Hỗ trợ resume upload nếu bị ngắt giữa chừng.
Tối ưu trải nghiệm người dùng (UX) và tài nguyên hệ thống.
🧱 Tổng quan kiến trúc đề xuất:
Client (browser/app)
⇅
Reverse Proxy (Nginx / CDN)
⇅
Upload API (Spring Boot)
⇅
File Storage (S3, GCS, MinIO, NAS...)
+
Database (metadata)
🧠 Chi tiết kỹ thuật từng phần:
1. Upload kiểu trực tiếp (Direct Upload to Storage) 🔄
Đối với file lớn, không nên truyền hết qua backend → dễ gây overload. Thay vào đó:
Backend chỉ tạo pre-signed URL (S3, GCS, MinIO...)
Client upload trực tiếp lên storage thông qua URL đó (giảm tải cho backend).
Sau khi upload xong, client gửi callback để thông báo hoàn tất.
✅ Ưu điểm: Giảm tải backend, tăng tốc độ upload, không timeout.
2. Hỗ trợ Multipart / Chunked Upload 📦
Để xử lý file lớn & đảm bảo resume khi mạng bị gián đoạn:
Client chia file ra từng chunk nhỏ (5-10MB)
Gửi từng chunk lên (song song hoặc tuần tự)
Backend/storages sẽ merge lại hoặc lưu metadata từng phần.
🔧 Ví dụ: S3 hỗ trợ
Multipart Upload
API; Google Drive cũng hoạt động tương tự.
3. Tạm lưu file ở Local nếu cần (Optional)
Trong trường hợp cần xử lý file (scan virus, convert, nén...), có thể lưu tạm ở local hoặc mounted volume:
/tmp/uploads/sessionId/part1.tmp
Sử dụng Streaming API để xử lý file mà không cần load toàn bộ vào RAM.
Dùng
InputStream
/StreamingResponseBody
trong Spring Boot để stream.
4. Quản lý trạng thái upload (Resume, Fail-safe)
Tạo bảng
upload_session
trong DB:
sqlCopyEditid | user_id | file_name | total_chunks | received_chunks | status | created_at
Mỗi lần client gửi chunk, cập nhật trạng thái.
Nếu bị mất kết nối, client có thể resume từ chunk chưa gửi.
5. Timeout & Stability
Reverse proxy như Nginx phải cấu hình:
client_max_body_size
proxy_read_timeout
/proxy_send_timeout
Spring Boot cấu hình:
propertiesCopyEditspring.servlet.multipart.max-file-size=2GB
spring.servlet.multipart.max-request-size=2GB
server.tomcat.max-swallow-size=-1
🛡️ Một số điểm cần lưu ý:
Quá tải RAM
Không lưu toàn bộ file vào memory (streaming)
Mất kết nối mạng
Hỗ trợ resume upload
Timeout HTTP
Dùng direct upload hoặc websocket/message queue
Quét virus
Scan sau khi upload xong
Kiểm tra loại file
Dựa vào header & magic bytes, không chỉ nhìn file extension
Tối ưu UX
Progress bar, tốc độ, resume, retry
📌 Công nghệ gợi ý:
Client
HTML5 File API + JavaScript (chunked upload, retry logic)
API
Spring Boot (REST + streaming), hoặc WebSocket nếu cần push
Storage
Amazon S3 / MinIO / GCS
CDN (optional)
CloudFront / Nginx cache
Background job
Redis queue + worker (convert, scan...)
🔄 Flow Tổng Thể Upload File Lớn (Chunked Upload + Backend-Storage Architecture)
📦 1. Client khởi tạo phiên upload
API:
POST /api/files/init-upload
Payload:
{ filename, size, contentType }
Backend xử lý:
Sinh ra
uploadId
, lưu metadata (filename, user, size, status = INITIATED)Trả về
uploadId
+chunkSize
+ thông tin định dạng(Tùy chọn): trả về pre-signed URLs (nếu dùng S3/MinIO)
✅ Tác dụng: quản lý session, theo dõi tiến độ, kiểm tra lỗi.
📤 2. Client chia file thành các chunk và upload từng phần
File sẽ được chia thành từng
chunk
(VD: 5MB/chunk)Client gửi tuần tự hoặc song song:
POST /api/files/upload-chunk
Headers:
Content-Range: bytes 0-5242879/1048576000
Body:
binary data (chunk)
Query params:
uploadId, chunkIndex
Backend lưu chunk vào:
Tạm thời: local/temp folder (disk, Redis if meta)
Hoặc trực tiếp đẩy từng chunk vào object storage (S3/MinIO/GCS)
📥 3. Client gọi hoàn tất upload
Sau khi upload xong toàn bộ chunk:
POST /api/files/complete-upload
Payload: { uploadId }
Backend sẽ:
Validate đủ chunk
Gộp các chunk lại (nếu local storage)
Hoặc trigger storage (nếu S3 multipart upload complete)
Tạo metadata: path, size, MIME, checksum...
Cập nhật trạng thái uploadId →
COMPLETED
⚠️ 4. Exception & Resilience Handling
⏱️ Timeout / mạng yếu
Cho phép client retry chunk upload từng phần
💔 Upload bị gián đoạn
Dựa vào uploadId
, resume từ missing chunks
❌ Thiếu chunk khi complete
Trả lỗi 409 Conflict
, kèm danh sách chunk thiếu
💾 Disk full (local temp chunk)
Cảnh báo sớm bằng monitoring + chuyển qua S3
🔒 Token expire trong pre-signed URL
Re-request pre-signed URL theo chunkIndex
💣 File quá giới hạn
Validate tại init-upload
, trả về 413 Payload Too Large
📎 Duplicate upload
Check hash (MD5/SHA-256), hỗ trợ dedup nếu cần
✅ 5. Tối ưu & bảo trì
Hiệu năng
Sử dụng parallel chunk upload (ở client + backend thread pool)
Memory tiết kiệm
Stream trực tiếp, không giữ cả file trong RAM
Tăng độ ổn định
Quản lý trạng thái từng chunk (upload progress, retry logic)
Lưu trữ tạm
Xoá chunk sau 24h nếu không hoàn thành (sử dụng scheduler)
File integrity
Dùng checksum (MD5/SHA256) để xác nhận sau khi merge
🗃️ Công nghệ khuyến nghị
Storage
Amazon S3, MinIO, GCP Storage, NFS
Chunk tracking
Redis (tạm thời), PostgreSQL (metadata)
Streaming
InputStream
, FileChannel
, MultipartFile.transferTo()
Large request
spring.servlet.multipart.max-file-size=-1
(tắt limit)
Retry logic
Client-side + Backend retry handler
Khuyến khích sử dụng S3 + Kafka để scale tốt nhất.
Last updated