使用第三方组件库实现文件上传,如react-dropzone。
将大文件分成多个小块,并使用XMLHttpRequest
或者fetch
发送分块上传请求。为了保证数据完整性,每个请求都需要携带校验码。在上传过程中需要实时统计上传进度。
后端接收到分块上传的请求后,将每个分块存储到指定路径下,并根据校验码判断分块是否完整。
当所有分块上传成功后,后端将分块整合成一个完整的文件,并删除已上传的分块。
当文件上传成功后,前端清除上传状态并显示成功的提示信息。
react-dropzone
和axios
实现的React大文件上传组件代码:import React, { useState } from 'react';
import { useDropzone } from 'react-dropzone';
import axios from 'axios';
const LargeFileUploader = () => {
const [uploadedFiles, setUploadedFiles] = useState([]);
const onDrop = async (acceptedFiles) => {
// 使用FormData来封装文件数据
const formData = new FormData();
acceptedFiles.forEach((file) => {
formData.append('files', file);
});
try {
// 发起上传请求
const response = await axios.post('/upload-url', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (progressEvent) => {
// 上传进度回调
const progress = Math.round(
(progressEvent.loaded / progressEvent.total) * 100
);
console.log(`Upload Progress: ${progress}%`);
},
});
// 上传成功后更新已上传文件列表
setUploadedFiles(response.data);
console.log('Upload Successful!');
} catch (error) {
console.error('Upload Error:', error);
}
};
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
return (
{isDragActive ? (
拖放文件到此处以上传
) : (
将文件拖放至此处,或点击选择文件进行上传
)}
{uploadedFiles.map((file, index) => (
- {file.name}
))}
);
};
export default LargeFileUploader;
在上面的代码中,我们创建了一个名为LargeFileUploader
的组件。它使用react-dropzone
来实现文件拖放和选择功能,并使用axios
发起上传请求。
在onDrop
方法中,我们首先将接收到的文件数据封装到FormData
对象中,然后使用axios.post
方法发送POST请求到指定的上传URL。我们设置请求头的Content-Type
为multipart/form-data
以支持文件上传。
在请求配置中,我们还通过onUploadProgress
参数传入一个回调函数,用于监视文件上传的进度并显示在控制台中。
上传成功后,我们将服务器返回的已上传文件列表更新到uploadedFiles
状态,并在页面中渲染出来。
最后,在渲染部分,我们使用useDropzone
hook获取所需的属性和事件绑定,并根据拖放状态显示相应提示信息和已上传文件列表。
需要注意的是,示例中的上传URL路径/upload-url
需要替换为自己的后端上传接口路径。另外,还需要对上传接口进行额外的处理和验证,以适应后端逻辑。
react-dropzone
和axios
的基础上进行扩展 ,在原有的基础上添加ChunkedFileUploader
组件,并引入一个常量CHUNK_SIZE
来指定切片大小(这里设置为1MB):import React, { useState } from 'react';
import { useDropzone } from 'react-dropzone';
import axios from 'axios';
const CHUNK_SIZE = 1024 * 1024; // 切片大小为1MB
const ChunkedFileUploader = () => {
const [uploadedChunks, setUploadedChunks] = useState({});
const [uploadedFile, setUploadedFile] = useState(null);
const [isUploading, setIsUploading] = useState(false);
const onDrop = (acceptedFiles) => {
const file = acceptedFiles[0];
setUploadedFile(file);
};
const uploadChunks = async () => {
if (!uploadedFile) return;
setIsUploading(true);
try {
const totalChunks = Math.ceil(uploadedFile.size / CHUNK_SIZE);
const chunksToUpload = [];
// 分割文件为切片
for (let i = 0; i < totalChunks; i++) {
const start = i * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, uploadedFile.size);
const chunk = uploadedFile.slice(start, end);
chunksToUpload.push(chunk);
}
// 依次上传切片
for (let i = 0; i < chunksToUpload.length; i++) {
const formData = new FormData();
formData.append('file', chunksToUpload[i]);
formData.append('chunkNumber', i + 1);
formData.append('totalChunks', totalChunks);
await axios.post('/upload-url', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
setUploadedChunks((prevChunks) => ({
...prevChunks,
[i + 1]: true,
}));
}
console.log('Upload Complete!');
} catch (error) {
console.error('Upload Error:', error);
} finally {
setIsUploading(false);
}
};
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
return (
{isDragActive ? (
拖放文件到此处以上传
) : (
将文件拖放至此处,或点击选择文件进行上传
)}
{uploadedFile && !isUploading && (
)}
{isUploading && 上传中...
}
{Object.keys(uploadedChunks).map((chunkNumber) => (
- {`切片 ${chunkNumber}`}
))}
);
};
export default ChunkedFileUploader;
在上传时,根据文件大小计算出切片的总数。然后,我们通过循环将文件分割成多个切片,并使用FormData
对象将每个切片进行封装。最后,通过循环依次上传每个切片,并在每个切片上传成功后更新已上传切片的状态。