功能实现
<template>
<input ref="inputFile" type="file" hidden @change="checkFiles" />
<van-button class="upBut" color="#BF234D" @click="onClick">点击上传文件</van-button>
</template>
<script setup lang='ts'>
import Apis from '@/utils/apis';
import FileReaderUpoad from "@/utils/upoad";
import { showLoadingToast, showSuccessToast, showFailToast } from 'vant';
const inputFile = ref<HTMLFieldSetElement | null>(null);
const data = reactive({
loading: false,
});
const onClick = () => {
inputFile.value && inputFile.value.click();
};
const checkFiles = (e: Event) => {
if (data.loading) return;
data.loading = true;
let event: any = e.target as EventTarget;
let file = event.files[0];
let storageId = "";
let fileName = "", suffix = "";
if (file.name) {
let arr = file.name.split('.');
fileName = arr.filter((item: object, index: number) => arr.length - 1 != index).join(".");
suffix = arr.filter((item: object, index: number) => arr.length - 1 == index).join(".");
};
showLoadingToast({
message: `正在解析文件`,
forbidClick: true,
duration: 0,
overlay: true,
});
let upoad = new FileReaderUpoad({
file,
maxNum: 4,
size: 5242880,
byteLength: file.size,
singleFileSize: 209715200,
once: () => {
showLoadingToast({
message: `正在上传中`,
forbidClick: true,
duration: 0,
overlay: true,
});
let obj: any = {...};
Apis.upLoad(obj).then((res: any) => {
if (res.code == 200) {
data.loading = false;
showSuccessToast("上传成功");
};
}).catch((err) => {
showFailToast("上传出现问题");
data.loading = false;
});
},
start: (data: any, next: Function) => {
let obj: any = {
fileMd5: data.md5,
sliceTotalChunks: data.total,
...
};
Apis.start(obj).then((res: any) => {
if (res.code == 200) {
storageId = res.obj.storageId;
next();
};
}).catch(() => {
showFailToast("创建上传信息失败");
data.loading = false;
});
},
upload: (val: any, next: Function) => {
let obj: any = {
name: fileName,
chunk: val.page,
chunks: val.total,
md5: val.totalMd5,
size: val.file.size,
tenantId: storageId,
currentFileMd5: val.md5,
file: val.blob
};
Apis.burst(obj).then((res: any) => {
if (res.code == 200 && res.success) next();
else next(val);
}).catch(() => {
next(val);
});
},
loading: (val: string) => {
showLoadingToast({
message: `已上传${val}%`,
forbidClick: true,
duration: 0,
overlay: true,
});
},
end: (res: any) => {
Apis.end({ tenantId: storageId }).then((res: any) => {
if (res.code == 200) {
showSuccessToast("上传成功");
}
}).catch(() => {
showFailToast("上传失败");
data.loading = false;
})
}
});
upoad.uploadStart();
};
</script>
import SparkMD5 from 'spark-md5';
interface FileType {
file: File;
size: number;
once?: Function;
maxNum?: number;
start?: Function;
end?: Function;
upload?: Function;
loading?: Function;
byteLength: number;
singleFileSize: number;
};
export default class FileReaderUpoad {
private option: any = [];
private Setting: FileType;
private totalFilm!: number;
private result: string = "";
private current: number = 0;
private totalMd5: string = '';
private arrLength: number = 0;
constructor(Setting: FileType) {
this.Setting = Setting;
};
public uploadStart = () => {
let that = this;
if (this.Setting.file.size >= this.Setting.singleFileSize) {
let fileReader = new FileReader();
fileReader.readAsBinaryString(this.Setting.file);
fileReader.onload = function () {
that.Setting.maxNum = that.Setting.maxNum || 1;
that.result = fileReader.result as string;
that.totalFilm = Math.ceil(that.Setting.byteLength / that.Setting.size);
that.getDataMd5(that.result).then((res: any) => {
that.totalMd5 = res;
if (typeof (that.Setting.start) === 'function') that.Setting.start({ md5: res, total: that.totalFilm }, () => {
that.current = 1;
that.option = [];
that.burstParam();
});
});
};
} else {
typeof this.Setting.once == 'function' && this.Setting.once();
};
};
private burstParam() {
if (this.current < this.totalFilm) {
let start = (this.current - 1) * this.Setting.size;
let end = this.current * this.Setting.size;
this.fileSlice(start, end, (val: string) => {
this.getDataMd5(val).then((res: unknown) => {
this.option.push({
md5: res,
chunkFile: val,
size: val.length,
page: this.current -1,
total: this.totalFilm,
totalMd5: this.totalMd5,
file: this.Setting.file,
blob: this.Setting.file.slice(start, end),
});
this.current++;
this.burstParam();
});
});
} else {
let start = (this.current - 1) * this.Setting.size;
let end = this.current * this.Setting.size;
this.fileSlice(start, end, (val: string) => {
this.getDataMd5(val).then((res: unknown) => {
this.option.push({
md5: res,
chunkFile: val,
size: val.length,
page: this.current -1,
total: this.totalFilm,
totalMd5: this.totalMd5,
file: this.Setting.file,
blob: this.Setting.file.slice(start, this.Setting.byteLength),
});
this.arrLength = this.option.length;
if (this.Setting.maxNum && this.Setting.maxNum > 0) {
this.multiRequest(this.option, this.Setting.maxNum).then((res: any) => {
typeof this.Setting.end == 'function' && this.Setting.end(res);
});
} else {
new Error("并发数不能为零!");
};
});
});
};
};
private fileSlice = (start: number, end: number, callback: Function) => {
callback(this.result.slice(start, end));
};
private getDataMd5 = (data: string) => {
return new Promise((resolve, reject) => {
if (data) resolve(new SparkMD5().appendBinary(data).end());
else reject("计算失败");
});
};
private multiRequest = (urls: any, maxNum: number) => {
let queue: any = [];
let urlsClone = [...urls];
let result = new Array(urls.length);
let isDoneArr = new Array(urls.length).fill(0);
let queueLimit = Math.min(maxNum, urls.length);
let index = 0;
const request = (queue: any, url: any) => {
if (typeof this.Setting.upload == 'function') this.Setting.upload(url, (val: object) => {
let i = urlsClone.indexOf(url);
result[i] = url;
isDoneArr[i] = 1;
if(val) {
this.arrLength++;
urls.push(val);
isDoneArr.push(val);
}
outLine(queue, url);
})
};
const EnterTheTeam = (queue: any = [], url: object) => {
let len = queue.push(url);
if (len == this.Setting.maxNum) {
index++;
let t = parseInt(String(index / this.arrLength * 100));
typeof this.Setting.loading == 'function' && this.Setting.loading(t);
};
request(queue, url);
return len;
};
while (EnterTheTeam(queue, urls.shift()) < queueLimit) {};
let promise: any = {
resolve: '',
reject: '',
};
const outLine = (queue: any = [], url: object) => {
queue.splice(queue.indexOf(url), 1);
if (urls.length) EnterTheTeam(queue, urls.shift());
else {
if (isDoneArr.indexOf(0) === -1) {
promise.resolve(result);
typeof this.Setting.loading == 'function' && this.Setting.loading(100);
};
}
};
return new Promise((resolve, reject) => {
return promise.resolve = resolve;
});
};
};