闽公网安备 35020302035485号



<template>
<Input type="file" @change="hanleInputChange" />
<Progress :percent="percent" />
<Button @click="start" type="primary" class="mr-2">开始</Button>
<Button @click="pause">暂停</Button>
<Button @click="keep">续传</Button>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { Button, Input, Progress } from 'ant-design-vue';
import axios, { type AxiosProgressEvent } from 'axios';
</script>
const file = ref<File | null>(null);
const hanleInputChange = (e: any) => {
// 堆代码 duidaima.com
// 把所需上传的文件先存起来
file.value = e.target.files[0];
};
6.记得把CancelToken放在上传axios中,用于后续的暂停请求
interface IFileChunk {
file: Blob;
size: number;
finish: boolean;
chunkName: string;
fileName: string;
index: number;
}
// 存储切片
const chunkList = ref<IFileChunk[]>([]);
// 用于axios请求的取消
const CancelToken = axios.CancelToken;
let source = CancelToken.source();
// 每个切片的尺寸
const SIZE = 3 * 1024 * 1024;
// 创建切片
const createChunks = () => {
const fileName = file.value!.name;
const list: IFileChunk[] = [];
let s = 0;
let index = 0;
while (s < file.value!.size) {
const fileChunk = file.value!.slice(s, s + SIZE);
list.push({
file: fileChunk,
size: fileChunk.size,
finish: false,
chunkName: `${fileName}-${index}`,
fileName,
index,
});
s += SIZE;
index++;
}
chunkList.value = list;
};
// 监听上传过程的回调
const onUploadProgress = (index: number, e: AxiosProgressEvent) => {
const chunkItem = chunkList.value[index];
const { loaded, total } = e;
if (loaded >= total!) {
// 满足这个条件,代表这个切片已经上传完成
chunkItem.finish = true;
}
};
// 上传的请求函数
const upload = async (list?: IFileChunk[]) => {
const fileList = list ?? chunkList.value;
if (!fileList.length) return;
return Promise.all(
fileList
.map(({ file, fileName, index, chunkName }) => {
const formData = new FormData();
formData.append('file', file);
formData.append('fileName', fileName);
formData.append('chunkName', chunkName);
return { formData, index };
})
.map(({ formData, index }) =>
axios.post('http://localhost:3000/upload', formData, {
onUploadProgress: e => {
onUploadProgress(index, e);
},
cancelToken: source.token,
}),
),
);
};
// 合并的请求函数
const merge = () =>
axios.post(
'http://localhost:3000/merge',
JSON.stringify({
size: SIZE,
fileName: file.value!.name,
}),
{
headers: {
'content-type': 'application/json',
},
},
);
// 开始上传
const start = async () => {
if (!file.value) return;
createChunks();
await upload();
await merge();
};
百分比计算const percent = ref(0);
// 监听切片列表的变化
watch(
() => chunkList,
v => {
// 计算出多少个已经上传完成
const finishChunks = v.value.filter(({ finish }) => finish);
// 计算百分比
percent.value = Number((finishChunks.length / v.value.length).toFixed(2)) * 100;
},
{
deep: true,
},
);
const pause = () => {
source.cancel('中断上传!');
source = CancelToken.source();
};
const verify = async () => {
const { data } = await axios.post(
'http://localhost:3000/verify',
JSON.stringify({
fileName: file.value!.name,
}),
{
headers: {
'content-type': 'application/json',
},
},
);
return data;
};
const keep = async () => {
const { shouldUpload, uploadedList } = await verify();
// shouldUpload = true 说明不用续传了
if (!shouldUpload) return;
// 计算出哪些切片没有上传
const uploadList = chunkList.value.filter(({ chunkName }) => !uploadedList.includes(chunkName));
// 进行续传
upload(uploadList);
};
const start = async () => {
if (!file.value) return;
+ const { shouldUpload } = await verify();
+ if (!shouldUpload) {
+ console.log('上传成功');
+ return;
+ }
createChunks();
await upload();
await merge();
};