1. 05:28 07/10/2026
## 1. Xác định các yêu cầu cốt lõi
Trước khi viết mã, hãy liệt kê các tính năng bạn cần:
- **Hỗ trợ nhiều loại tệp** (hình ảnh, tài liệu, video).
- **Kích thước và loại tệp được xác thực** ở cả phía máy khách và phía máy chủ.
- **Tiến trình tải lên có thể theo dõi** với thanh tiến trình trực quan.
- **Tải lên theo đoạn** cho các tệp lớn để tránh giới hạn kích thước kết nối.
- **Xem trước và xác thực thời gian thực** (ví dụ: kiểm tra kích thước hình ảnh ngay lập tức).
- **Xử lý lỗi mạnh mẽ** với thông báo rõ ràng cho người dùng.
- **Tích hợp liền mạch** với các dịch vụ lưu trữ bên thứ ba (ví dụ: AWS S3, Cloudinary).
## 2. Xây dựng cấu trúc upload linh hoạt
### a. Thư viện phía máy khách
Sử dụng các thư viện đã được kiểm chứng như **react‑dropzone** (React) và **react‑native‑document‑picker** / **react‑native‑fs** (React Native). Chúng đơn giản hóa việc xử lý kéo thả, chọn tệp và cung cấp các hook để tích hợp logic tải lên tùy chỉnh.
### b. Xác thực phía máy khách
Tạo một dịch vụ xác thực tập trung để tái sử dụng:
```ts
function validateFile(file: File): ValidationError | null {
const maxSize = 10 * 1024 * 1024; // 10 MB
if (file.size > maxSize) return { code: 'SIZE', message: 'File vượt quá 10 MB' };
const allowed = ['image/jpeg', 'image/png', 'application/pdf'];
if (!allowed.includes(file.type)) return { code: 'TYPE', message: 'Loại tệp không được hỗ trợ' };
return null;
}
```
### c. Tải lên theo đoạn
Đối với các tệp lớn hơn 5 MB, hãy chia nhỏ tệp thành các chunk có kích thước 1 MB và tải lên từng chunk song song. Hook `useUploadChunked` sau đây minh họa quy trình này:
```ts
async function uploadChunked(file: File, onProgress: (p: number) => void) {
const chunkSize = 1 * 1024 * 1024;
const totalChunks = Math.ceil(file.size / chunkSize);
const formData = new FormData();
for (let i = 0; i < totalChunks; i++) {
const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
formData.append('chunk', chunk, `${file.name}.part${i}`);
formData.append('index', String(i));
formData.append('total', String(totalChunks));
await fetch('/api/upload-chunk', { method: 'POST', body: formData });
onProgress(((i + 1) / totalChunks) * 100);
}
}
```
### d. Phản hồi trực quan cho người dùng
Sử dụng React state (`UploadStatus`) để hiển thị:
- **Vùng kéo thả** với các tín hiệu trực quan (ví dụ: viền màu xanh lam).
- **Danh sách tệp** với tên, kích thước, thanh tiến trình và biểu tượng thành công/lỗi.
- **Thông báo lỗi** thân thiện, giải thích lý do (ví dụ: "Định dạng tệp không hợp lệ").
## 3. Xử lý phía máy chủ
- **Chấp nhận chunk** trong endpoint API (`/api/upload-chunk`) và lưu trữ chúng trong bộ nhớ tạm.
- **Tổng hợp** tất cả các chunk sau khi tải lên hoàn tất, sau đó xóa các chunk tạm thời.
- **Trả về URL** của tệp đã hoàn tất để tham chiếu trong tương lai.
Sử dụng middleware xác thực để thực hiện kiểm tra kích thước tệp và loại MIME một lần nữa nhằm đảm bảo tính nhất quán.
## 4. Lời khuyên thực tế để đạt hiệu suất cao
- **Giới hạn số lượng tệp tải lên đồng thời** (ví dụ: không quá 3) để tránh quá tải bộ nhớ.
- **Bộ nhớ đệm URL đối tượng** cho các tệp nhỏ (`URL.createObjectURL`) để xem trước ngay lập tức mà không cần tải lên.
- **Bỏ qua xác thực phía máy khách** cho các trường hợp quan trọng; luôn xác thực lại ở phía máy chủ.
- **Tận dụng CDN** để phân phối các tệp đã tải lên, giảm độ trễ khi truy xuất.
- **Ghi nhật ký từng bước tải lên** (bắt đầu, chunk hoàn tất, chunk bị thiếu) để hỗ trợ việc khắc phục sự cố trong môi trường sản xuất.
## 5. Tổng kết
Một quy trình upload file mạnh mẽ kết hợp các pattern tải lên theo đoạn, xác thực thời gian thực và phản hồi trực quan giúp cải thiện đáng kể trải nghiệm người dùng. Bằng cách giữ cho mã nguồn có cấu trúc rõ ràng, tái sử dụng các tiện ích xác thực và xử lý các lỗi ở cả hai phía, bạn có thể xây dựng một giải pháp có khả năng mở rộng, hoạt động tốt trên cả React web và React Native mobile apps.
---