import generateId from '@/utils/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 | 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 } }