为什么要分片上传
上传大文件(比如3GB)时,一般不能调用一次接口上传完,接口会超时或报错,这时候需要前端把大文件分片处理一下,再分别调用接口上传分片文件,全部分片上传完后,由后端将所有分片合并和返回最终的文件地址。
实现思路
- 在进行分片上传前,需要对文件进行md5加密,生成md5码,在后面每次调用接口时以formData格式上传给接口
- 然后定义初始化信息,比如分片大小、开始和结束的文件大小、索引
- 接着执行定义好的分片函数,传入初始化好的文件信息:uploadChunk(file, start, end, index);
- 最后执行uploadChunk(file, start, end, index),先调用分片接口上传分片文件,所有分片文件全部上传后调用合并分片接口,该接口会返回整个文件上传后最终的地址
- file.slice(start, end)直接对文件进行分片,第一次分片的start是0,也就是文件开始位置,end是分片的大小(如果文件没有分片大就是文件大小)
- 在文件分片后,将分片信息以formData格式作为参数传给分片接口
- 在接口成功响应后,更新开始和结束位置、index自增:开始位置更新为上次的结束位置大小,结束位置更新为上次的结束位置加一个分片后的大小(如果加起来比文件大,结束位置就是整个文件大小),
- 判断是不是最后一个分片:比较start 和 file.size大小,如果更新后的文件开始大小比整个文件小,递归调用uploadChunk,传入更新后的参数,直到所有分片都上传;如果更新后的文件开始大小比整个文件大(即上次end结束位置已经上传全部分片了),说明分片已经全部上传完了,不再调用分片接口,调用合并分片接口,获取最终的整个文件地址
async handleAvatarUploadDemo({
file }) {
.....
//MD5加密
const md5 = await this.handleFile(file);
const chunkSize = 10 * 1024 * 1024; // 10MB 每个分片的大小
let start = 0;
let end = Math.min(chunkSize, file.size);
let index = 0;
const uploadChunk = (file, start, end, index) => {
const chunk = file.slice(start, end); // 切割文件为分片
let formData = new FormData();
formData.append('file', chunk);
formData.append('index', index);
formData.append('totalChunks', Math.ceil(file.size / chunkSize));
formData.append('md5', md5);
axios
.post(
'/api/spang-system/oss/endpoint/put-file-flake',
formData, // 直接传递formData对象
{
headers: {
'Content-Type': 'multipart/form-data' },
timeout: 600000,
}
)
.then(res => {
// 处理分片上传成功的响应
index++;
start = end;
end = Math.min(start + chunkSize, file.size);
if (file.size - start < 5 * 1024 * 1024 && start !== 0) {
// 最后一个切片大小小于5MB且不是第一个切片
start = end;
end = file.size; // 设置结束位置为文件末尾start = Math.max(0, end - chunkSize); // 重新计算起始位置
}
if (start < file.size) {
uploadChunk(file, start, end, index); // 递归上传下一个分片,并传递file参数
} else {
// 所有分片上传完成
// 合并分片等操作
const param = {
md5: md5, originalFilename: file