166 lines
3.4 KiB
TypeScript
166 lines
3.4 KiB
TypeScript
import generateId from "@/hooks/generateId";
|
|
import useMiaoFetchApi from "@/hooks/useMiaoFetchApi";
|
|
import VirtualDirectory from "./VirtualDirectory";
|
|
|
|
const miaoFetchApi = useMiaoFetchApi();
|
|
|
|
export class UploadQueueItem {
|
|
constructor(
|
|
file: File,
|
|
targetDirectory: VirtualDirectory,
|
|
config?: {
|
|
onSuccess?: () => void;
|
|
onFailed?: () => void;
|
|
}
|
|
) {
|
|
this.file = file;
|
|
this.targetDirectory = targetDirectory;
|
|
this.uid = generateId();
|
|
this.status = "waiting";
|
|
this.process = 0;
|
|
this.onSuccess = config && config.onSuccess;
|
|
this.onFailed = config && config.onFailed;
|
|
}
|
|
uid: number;
|
|
file: File;
|
|
targetDirectory: VirtualDirectory;
|
|
status: "waiting" | "uploading" | "success" | "failed";
|
|
process: number;
|
|
failedReason: unknown | undefined;
|
|
|
|
onSuccess?: () => void;
|
|
onFailed?: () => void;
|
|
|
|
_abort: (() => void | undefined) | undefined;
|
|
_status: Promise<any> | undefined;
|
|
|
|
/**
|
|
* 开始上传文件
|
|
*/
|
|
startUpload() {
|
|
if (this.status === "uploading") {
|
|
return;
|
|
}
|
|
const { abort, response } = miaoFetchApi.upload(this.file, this.targetDirectory, {
|
|
onProcess: percent => {
|
|
this.process = percent;
|
|
}
|
|
});
|
|
this._abort = abort;
|
|
this._status = response;
|
|
this.status = "uploading";
|
|
response
|
|
.then(() => {
|
|
this.status = "success";
|
|
this.onSuccess && this.onSuccess();
|
|
this._abort = undefined;
|
|
this._status = undefined;
|
|
})
|
|
.catch(e => {
|
|
this.status = "failed";
|
|
this.failedReason = e;
|
|
this.onFailed && this.onFailed();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 终止上传文件
|
|
*/
|
|
abortUpload() {
|
|
this._abort && this._abort();
|
|
this.status = "failed";
|
|
this.failedReason = "abort";
|
|
}
|
|
}
|
|
|
|
export default class UploadQueue {
|
|
constructor() {
|
|
this.uid = generateId();
|
|
this.queue = [];
|
|
this.mode = "serial";
|
|
}
|
|
uid: number;
|
|
queue: UploadQueueItem[];
|
|
current: UploadQueueItem | undefined;
|
|
|
|
// 未来再去实现,目前就是串行的
|
|
// 好像也不用去实现
|
|
mode: "serial" | "parallel";
|
|
get status() {
|
|
for (let item of this.queue) {
|
|
if (item.status === "uploading") {
|
|
return "busy";
|
|
}
|
|
}
|
|
return "free";
|
|
}
|
|
|
|
/**
|
|
* 获取下一个需要被上传的item
|
|
*/
|
|
get nextUplpadItem() {
|
|
for (let item of this.queue) {
|
|
if (item.status === "waiting") {
|
|
return item;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始串行的上传循环
|
|
*/
|
|
loop() {
|
|
if (this.status === "busy") {
|
|
return;
|
|
}
|
|
if (this.nextUplpadItem) {
|
|
this.nextUplpadItem.startUpload();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 向上传文件队列输送一批新的文件
|
|
* @param {any} files 需要上传的文件
|
|
* @param {any} targetDirectory 目标文件夹
|
|
* @param {any} option 每个文件上传完成以后都会触发一遍的回调函数
|
|
* @returns {any}
|
|
*/
|
|
push(
|
|
files: File[],
|
|
targetDirectory: VirtualDirectory,
|
|
option?: {
|
|
/**
|
|
* 每个文件上传完成以后都会触发一遍的回调函数
|
|
*/
|
|
onFinish?: () => void;
|
|
/**
|
|
* 是否立即开始上传,若否则按队列顺序一个一个上传
|
|
*/
|
|
immediately?: boolean;
|
|
}
|
|
) {
|
|
const onFinish = option?.onFinish;
|
|
const immediately = option?.immediately ?? false;
|
|
const uploadQueue = files.map(file => {
|
|
return new UploadQueueItem(file, targetDirectory, {
|
|
onSuccess: () => {
|
|
this.loop();
|
|
onFinish && onFinish();
|
|
},
|
|
onFailed: () => {
|
|
this.loop();
|
|
}
|
|
});
|
|
});
|
|
this.queue.push(...uploadQueue);
|
|
if (!immediately) {
|
|
this.loop();
|
|
} else {
|
|
uploadQueue.forEach(item => {
|
|
item.startUpload();
|
|
});
|
|
}
|
|
return uploadQueue;
|
|
}
|
|
}
|