Khi xây dựng các ứng dụng web hoặc di động cần truyền tải các tệp lớn (ví dụ: video, tệp CAD, bản ghi âm), phương pháp upload truyền thống (tải toàn bộ tệp trong một lần) nhanh chóng bộc lộ hạn chế: thời gian chờ lâu, tỷ lệ thất bại cao và trải nghiệm người dùng kém. Bằng cách kết hợp **upload theo đoạn** ở phía máy khách với **truyền phát phản hồi từ máy chủ**, bạn có thể duy trì giao diện người dùng phản hồi nhanh, xử lý đáng tin cậy các tệp lớn và cung cấp cho người dùng khả năng xem trước tiến trình. **1. Tại sao nên sử dụng upload theo đoạn? - Độ tin cậy: Nếu mạng không ổn định, chỉ đoạn dữ liệu bị mất cần được truyền lại. - Tính khả thi: Các tệp lớn hơn nhiều giới hạn kích thước mặc định của multipart/form-data. - Khả năng mở rộng: Máy chủ có thể bắt đầu xử lý sớm, giảm thời gian chờ tổng thể. **2. Cấu trúc phía máy khách (React + React Native) - Cài đặt thư viện: `npm install react‑dropzone axios` - Tạo hook để chia nhỏ tệp: ```javascript const CHUNK_SIZE = 1024 * 1024; // 1 MB function useChunkedUpload(file) { const [progress, setProgress] = useState(0); const [stages, setStages] = useState([]); const uploadChunks = async () => { const totalChunks = Math.ceil(file.size / CHUNK_SIZE); const uploadId = uuidv4(); for (let i = 0; i < totalChunks; i++) { const chunk = file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE); const formData = new FormData(); formData.append('chunk', chunk); formData.append('uploadId', uploadId); formData.append('chunkIndex', i); formData.append('totalChunks', totalChunks); await axios.post('/api/upload/chunk', formData, { onUploadProgress: (e) => { const chunkProgress = (e.loaded / e.total) * 100; setStages(prev => { const copy = [...prev]; copy[i] = (copy[i] || 0) + chunkProgress; return copy; }); } }); setProgress(((i + 1) / totalChunks) * 100); } // Gọi API để hoàn tất await axios.post('/api/upload/finish', { uploadId }); }; return { progress, stages, uploadChunks }; } ``` **3. Xử lý phía máy chủ (ví dụ Node/Express) - Tạo một route `/api/upload/chunk` nhận dữ liệu dạng multipart, lưu từng đoạn vào bộ nhớ tạm hoặc cloud storage với tên duy nhất (uploadId + .chunkIndex). - Route `/api/upload/finish` tập hợp các đoạn theo thứ tự, tái tạo tệp gốc và xóa các đoạn tạm thời. - Cung cấp một endpoint để truy xuất tệp đã hoàn tất (hoặc để truyền phát trực tiếp qua `res.sendFile`). Sử dụng middleware `multer` với `storage: multer.memoryStorage()` để xử lý từng đoạn. **4. Mẹo thực tế - **Xác thực trước**: Sử dụng các thư viện như `file‑type‑validator` để kiểm tra kích thước, loại MIME và hàm băm để phát hiện giả mạo. - **Tiến trình hiển thị**: Hiển thị một thanh tiến trình tổng thể cùng với các thanh tiến trình nhỏ cho từng đoạn để thể hiện hoạt động song song. - **Thử lại tự động**: Lưu trạng thái từng đoạn vào Redux/Pinia; khi có lỗi mạng, chỉ cần tải lại đoạn bị lỗi. - **Truyền phát phản hồi**: Sau khi hoàn tất từng đoạn, máy chủ có thể gửi sự kiện `push` đến máy khách (WebSocket/Socket.io) để cập nhật giao diện người dùng ngay lập tức. - **Kiểm soát tốc độ tải lên**: Áp dụng giới hạn tốc độ tải lên ở phía máy chủ để tránh quá tải. **5. Lợi ích mang lại - **Phản hồi nhanh**: Giao diện người dùng không bao giờ bị chặn; người dùng có thể tiếp tục làm việc trong khi tải lên. - **Độ tin cậy cao**: Các đoạn bị mất có thể được tải lại mà không cần truyền lại toàn bộ tệp. - **Hiển thị rõ ràng**: Các thanh tiến trình từng đoạn cho người dùng thấy chính xác dữ liệu nào đã được truyền tải. Bằng cách kết hợp upload theo đoạn ở phía máy khách với xử lý từng đoạn ở phía máy chủ, bạn có thể xây dựng các ứng dụng React và React Native xử lý các tệp lớn một cách mượt mà và đáng tin cậy, mang lại trải nghiệm người dùng chuyên nghiệp. **6. Tài nguyên bổ sung - react‑dropzone: https://react‑dropzone.js.org/ - axios: https://axios-http.com/ - Multer: https://github.com/express‑multer/multer - Socket.io: https://socket.io/ Những thư viện này, khi được kết hợp với quy trình được mô tả ở trên, sẽ tạo nên một giải pháp upload tệp hiện đại, có khả năng mở rộng cho các ứng dụng web và di động hiện nay.