1. 18:29 09/10/2026
Quy trình upload file là một trong những tính năng phổ biến trong hầu hết các ứng dụng web và di động hiện đại. Việc xây dựng quy trình này một cách mạnh mẽ và có khả năng mở rộng không chỉ giúp cải thiện trải nghiệm người dùng mà còn đảm bảo sự ổn định của hệ thống khi xử lý nhiều tệp cùng lúc.
**1. Xác định các yêu cầu chính**
- **Kích thước và loại tệp**: Kiểm tra kích thước tối đa, giới hạn số lượng tệp và danh sách các MIME type được phép.
- **Theo dõi tiến trình**: Cung cấp cho người dùng phản hồi trực quan về tiến trình tải lên.
- **Xử lý lỗi**: Phát hiện các lỗi mạng, lỗi máy chủ hoặc lỗi xác thực và cung cấp các tùy chọn để thử lại.
- **Tái sử dụng logic**: Chia sẻ validation, xử lý tiến trình và tích hợp server giữa các dự án React web và React Native.
**2. Validation ở phía client**
Hãy bắt đầu bằng một custom hook có thể tái sử dụng:
```js
// useFileValidation.js
import { useState, useCallback } from 'react';
const useFileValidation = (maxSize, acceptedTypes) => {
const [error, setError] = useState(null);
const validate = useCallback((file) => {
if (!file) {
setError('Không có tệp nào được chọn.');
return false;
}
if (maxSize && file.size > maxSize) {
setError(`Kích thước tệp vượt quá giới hạn ${maxSize} byte.`);
return false;
}
if (acceptedTypes && !acceptedTypes.includes(file.type)) {
setError('Loại tệp không được hỗ trợ.');
return false;
}
setError(null);
return true;
}, [maxSize, acceptedTypes]);
return { error, validate };
};
```
Hook này có thể được sử dụng trong cả React web (qua HTMLInputElement) và React Native (qua react-native-document-picker).
**3. Theo dõi tiến trình tải lên**
Đối với React web, API FormData hỗ trợ sự kiện progress:
```js
const upload = async (file) => {
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (e) => {
const percent = Math.round((e.loaded / e.total) * 100);
// Cập nhật trạng thái tiến trình
});
// Xử lý kết quả
};
```
Trong React Native, bạn có thể sử dụng `fetch` với đối tượng `progress` tương tự hoặc thư viện `aws-sdk` cho các trường hợp sử dụng S3.
**4. Xử lý lỗi và thử lại**
Tạo một wrapper component bao quanh quy trình tải lên:
```js
const RobustUploader = ({ onSuccess }) => {
const [uploading, setUploading] = useState(false);
const [progress, setProgress] = useState(0);
const { error, validate } = useFileValidation(5 * 1024 * 1024, ['image/*']);
const handleUpload = async (selectedFile) => {
if (!validate(selectedFile)) return;
setUploading(true);
try {
await uploadWithRetry(selectedFile, { progress: setProgress, onSuccess });
} catch (e) {
// Hiển thị thông báo lỗi
} finally {
setUploading(false);
}
};
const uploadWithRetry = async (file, callbacks, attempts = 3) => {
for (let i = 0; i < attempts; i++) {
try {
await performUpload(file, callbacks);
return;
} catch (err) {
if (i === attempts - 1) throw err;
// chờ trước khi thử lại
}
}
};
};
```
**5. Chia sẻ logic thông qua Context**
Tạo một `UploadContext` để cung cấp state upload, hàm validation và hàm upload cho toàn bộ cây component. Điều này giúp các screen hoặc native modules có thể truy cập cùng một logic mà không cần prop drilling.
**6. Triển khai và kiểm tra**
- **Kiểm tra validation**: Sử dụng Jest + React Testing Library để mô phỏng các tệp hợp lệ và không hợp lệ.
- **Kiểm tra tiến trình**: Mô phỏng sự kiện `progress` của XHR/fetch.
- **Kiểm tra tích hợp server**: Sử dụng Mock Service Worker để mô phỏng các endpoint thành công, lỗi 5xx và giới hạn tốc độ mạng.
Bằng cách tuân thủ các bước trên, bạn có thể xây dựng quy trình upload file vừa mạnh mẽ vừa linh hoạt, hoạt động đồng nhất trên React web và React Native. Điều này không chỉ mang lại trải nghiệm người dùng mượt mà mà còn tạo nền tảng cho các tính năng nâng cao trong tương lai (như upload hàng loạt, xử lý trước tệp trên client hoặc tích hợp cloud).