UNPKG

js-uploader

Version:
608 lines 31 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Uploader = void 0; var tslib_1 = require("tslib"); var interface_1 = require("../interface"); var modules_1 = require("./modules"); var handlers_1 = require("./handlers"); var operators_1 = require("rxjs/operators"); var rxjs_1 = require("rxjs"); var Base_1 = require("./Base"); var utils_1 = require("../utils"); var helpers_1 = require("./helpers"); var shared_1 = require("../shared"); // import * as packageJson from '../../package.json' var defaultOptions = { requestOptions: { url: '/', timeout: 0, }, autoUpload: false, computeFileHash: false, computeChunkHash: false, maxRetryTimes: 3, retryInterval: 5000, chunked: true, chunkSize: 1024 * 1024 * 4, chunkConcurrency: 1, taskConcurrency: 1, resumable: true, }; var Uploader = /** @class */ (function (_super) { tslib_1.__extends(Uploader, _super); // static readonly version = (packageJson as any).version function Uploader(options) { var _this = _super.call(this, options === null || options === void 0 ? void 0 : options.id) || this; _this.taskQueue = []; _this.filePickers = []; _this.fileDraggers = []; _this.taskHandlerMap = new Map(); _this.upload$ = null; _this.subscription = new rxjs_1.Subscription(); _this.uploadSubscription = null; _this.taskSubject = new rxjs_1.Subject(); _this.action = new rxjs_1.Subject(); _this.pause$ = _this.action.pipe(operators_1.filter(function (v) { return v === 'pause'; })); _this.clear$ = _this.action.pipe(operators_1.filter(function (v) { return v === 'clear'; })); var opt = _this.mergeOptions(options); _this.validateOptions(opt); _this.options = opt; _this.id = _this.options.id; _this.initFilePickersAndDraggers(); _this.initEventHandler(); _this.options.resumable && _this.restoreTask(); return _this; } Uploader.create = function (options) { return new Uploader(options); }; Uploader.prototype.mergeOptions = function (options) { var opt = Object.assign({}, defaultOptions, options); Object.keys(defaultOptions).forEach(function (key) { var _a; var k = key; if (typeof defaultOptions[k] === 'object') { Object.assign(defaultOptions[k], opt[k]); var val = Object.assign({}, defaultOptions[k], opt[k]); Object.assign(opt, (_a = {}, _a[k] = val, _a)); } }); return opt; }; Uploader.prototype.validateOptions = function (options) { shared_1.Logger.info('🚀 ~ file: Uploader.ts ~ line 84 ~ Uploader ~ validateOptions ~ options', options); // TODO if (typeof options !== 'object') { throw new Error(''); } }; Uploader.prototype.upload = function (task, action) { var _this = this; var _a, _b; if (!this.uploadSubscription || ((_a = this.uploadSubscription) === null || _a === void 0 ? void 0 : _a.closed)) { var filteredTaskStatus_1 = [interface_1.StatusCode.Waiting, interface_1.StatusCode.Uploading, interface_1.StatusCode.Complete]; this.upload$ = this.taskSubject.pipe(operators_1.filter(function (task) { return !filteredTaskStatus_1.includes(task.status); }), operators_1.tap(function (task) { shared_1.Logger.info('🚀 ~ file: 等待上传', task); _this.changeUploadTaskStatus(task, interface_1.StatusCode.Waiting); _this.emit(interface_1.EventType.TaskWaiting, task); }), operators_1.mergeMap(function (task) { return _this.executeForResult(task, action); }, this.options.taskConcurrency || 1)); (_b = this.uploadSubscription) === null || _b === void 0 ? void 0 : _b.unsubscribe(); this.uploadSubscription = this.upload$.subscribe({ next: function (v) { shared_1.Logger.info('任务结束', v); _this.checkComplete(); }, error: function (e) { shared_1.Logger.info('任务出错', e); }, complete: function () { shared_1.Logger.info('所有任务完成'); }, }); this.subscription.add(this.uploadSubscription); } this.putNextTask(task); }; Uploader.prototype.checkComplete = function () { if (this.isComplete()) { this.emit(interface_1.EventType.Complete); } }; Uploader.prototype.executeForResult = function (task, action) { var _this = this; return rxjs_1.of(task).pipe(operators_1.filter(function (task) { return task.status === interface_1.StatusCode.Waiting; }), operators_1.concatMap(function () { return _this.getTaskHandler(task); }), operators_1.tap(function (handler) { handler = _this.rebindTaskHandlerEvent(handler); action === 'resume' ? handler.resume() : action === 'retry' ? handler.retry() : handler.handle(); }), operators_1.concatMap(function (handler) { return rxjs_1.race(rxjs_1.fromEvent(handler, interface_1.EventType.TaskPause).pipe(operators_1.tap(function () { // 暂停 shared_1.Logger.info('任务暂停'); })), rxjs_1.fromEvent(handler, interface_1.EventType.TaskCancel).pipe(operators_1.tap(function () { // 取消 _this.freeHandler(task); })), rxjs_1.fromEvent(handler, interface_1.EventType.TaskComplete).pipe(operators_1.tap(function () { // 完成 _this.freeHandler(task); })), rxjs_1.fromEvent(handler, interface_1.EventType.TaskError).pipe(operators_1.switchMap(function (err) { // 出错 跳过错误的任务? if (_this.options.skipTaskWhenUploadError) { _this.freeHandler(task); return rxjs_1.of(null); } return rxjs_1.throwError(err); }))).pipe(operators_1.mapTo(task), operators_1.first()); })); }; Uploader.prototype.resume = function (task) { this.upload(task, 'resume'); }; Uploader.prototype.retry = function (task) { this.upload(task, 'retry'); }; Uploader.prototype.pause = function (task) { var _this = this; this.action.next('pause'); var fn = function (task) { var _a; var handler = (_a = _this.taskHandlerMap.get(task.id)) === null || _a === void 0 ? void 0 : _a.pause(); if (!handler) { task.status = interface_1.StatusCode.Pause; // 任务暂停事件 _this.emit(interface_1.EventType.TaskPause, task); } }; var tasks = task ? [task] : this.taskQueue; var filteredStatus = [interface_1.StatusCode.Pause, interface_1.StatusCode.Complete, interface_1.StatusCode.Error]; rxjs_1.from(tasks) .pipe(operators_1.filter(function (tsk) { return !filteredStatus.includes(tsk.status); }), operators_1.tap(function (tsk) { return fn(tsk); })) .subscribe({ complete: function () { _this.emit(interface_1.EventType.TasksPause); }, }); }; Uploader.prototype.cancel = function (task) { if (task) { this.action.next('cancel'); return this.removeTask(task); } else { return this.clear(); } }; Uploader.prototype.clear = function () { var _this = this; return new Promise(function (resolve) { _this.action.next('clear'); var unsubscribe = function () { var _a; (_a = _this.uploadSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe(); _this.uploadSubscription = null; _this.upload$ = null; }; unsubscribe(); if (_this.taskQueue.length === 0) { return resolve(); } rxjs_1.of(_this.taskQueue.splice(0, _this.taskQueue.length)) .pipe(operators_1.tap(function () { return _this.emit(interface_1.EventType.Clear); }), operators_1.concatMap(function (list) { return _this.removeTask(list, true); }), operators_1.concatMap(function () { return _this.clearStorage(_this.id); }), operators_1.tap(function () { return modules_1.FileStore.clear(); })) .subscribe({ complete: function () { unsubscribe(); resolve(); }, }); }); }; Uploader.prototype.cancelFile = function (item) { var _this = this; var _a; var task = item.task, files = item.files; var handler = (_a = this.taskHandlerMap.get(task.id)) === null || _a === void 0 ? void 0 : _a.abortFile.apply(_a, tslib_1.__spread(files)); var rawTask = this.taskQueue.find(function (i) { return i.id === task.id; }); if (!handler && rawTask) { files.forEach(function (file) { var idIndex = rawTask === null || rawTask === void 0 ? void 0 : rawTask.fileIDList.indexOf(file.id); if (idIndex > -1) { rawTask === null || rawTask === void 0 ? void 0 : rawTask.fileIDList.splice(idIndex, 1); rawTask.fileSize -= file.size; _this.emit(interface_1.EventType.FileCancel, rawTask, modules_1.FileStore.get(file.id)); } var fileIndex = rawTask === null || rawTask === void 0 ? void 0 : rawTask.fileList.findIndex(function (i) { return i.id === file.id; }); if (fileIndex !== -1) { rawTask === null || rawTask === void 0 ? void 0 : rawTask.fileList.splice(fileIndex, 1); } _this.emit(interface_1.EventType.TaskUpdate, rawTask); }); this.emit(interface_1.EventType.FilesCancel, rawTask, files); } this.once(interface_1.EventType.FilesCancel, function () { if (_this.options.resumable) { _this.presistTaskOnly(task); _this.removeFileFromStroage.apply(_this, tslib_1.__spread(files)); _this.removeChunkFromStroage.apply(_this, tslib_1.__spread(files.reduce(function (arr, i) { return arr.concat(i.chunkIDList); }, []))); } _this.removeFileFromFileStore.apply(_this, tslib_1.__spread(files.map(function (i) { return i.id; }))); }); }; Uploader.prototype.isUploading = function () { var _a; if ((_a = this.uploadSubscription) === null || _a === void 0 ? void 0 : _a.closed) { return false; } return this.taskQueue.some(function (task) { return task.status === interface_1.StatusCode.Uploading || task.status === interface_1.StatusCode.Waiting; }); }; Uploader.prototype.hasError = function () { return this.taskQueue.some(function (task) { return task.status === interface_1.StatusCode.Error; }); }; Uploader.prototype.getErrorTasks = function () { return this.taskQueue.filter(function (task) { return task.status === interface_1.StatusCode.Error; }); }; Uploader.prototype.isComplete = function () { var status = [interface_1.StatusCode.Uploading, interface_1.StatusCode.Waiting, interface_1.StatusCode.Pause]; return !this.taskQueue.some(function (task) { return status.includes(task.status); }); }; Uploader.prototype.destory = function () { this.subscription.unsubscribe(); }; Uploader.prototype.removeTask = function (tasks, clear) { var _this = this; if (!Array.isArray(tasks)) { tasks = [tasks]; } return rxjs_1.scheduled(tasks || [], rxjs_1.asyncScheduler) .pipe(operators_1.tap(function (task) { var _a; var index = _this.taskQueue.findIndex(function (i) { return i.id === (task === null || task === void 0 ? void 0 : task.id); }); index > -1 && _this.taskQueue.splice(index, 1); (_a = _this.taskHandlerMap.get(task.id)) === null || _a === void 0 ? void 0 : _a.abort(); _this.taskHandlerMap.delete(task.id); !clear && _this.removeTaskFromStroage(task); // 任务取消事件 _this.emit(interface_1.EventType.TaskCancel, task); })) .toPromise(); }; Uploader.prototype.rebindTaskHandlerEvent = function (handler) { var _this = this; var e = []; for (var _i = 1; _i < arguments.length; _i++) { e[_i - 1] = arguments[_i]; } var events = (e === null || e === void 0 ? void 0 : e.length) ? e : Object.values(interface_1.EventType); events.forEach(function (e) { handler === null || handler === void 0 ? void 0 : handler.off(e); handler === null || handler === void 0 ? void 0 : handler.on(e, function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return _this.taskHandlerEventCallback.apply(_this, tslib_1.__spread([e], args)); }); }); return handler; }; Uploader.prototype.getTaskHandler = function (task) { var _this = this; return rxjs_1.iif(function () { return _this.taskHandlerMap.has(task.id); }, rxjs_1.of(this.taskHandlerMap.get(task.id)), handlers_1.handle(task, this.options)).pipe(operators_1.tap(function (handler) { _this.taskHandlerMap.set(task.id, handler); })); }; Uploader.prototype.freeHandler = function (task) { var id = task.id; var handler = this.taskHandlerMap.get(id) || null; if (handler) { Object.values(interface_1.EventType).forEach(function (e) { return handler.off(e); }); this.taskHandlerMap.delete(id); handler = null; } }; Uploader.prototype.putNextTask = function (task) { var _this = this; if (task) { if (typeof task === 'object') { this.taskSubject.next(task); } else { var tsk = this.taskQueue.find(function (tsk) { return tsk.id === task; }); tsk && this.taskSubject.next(tsk); } } else { var sub_1 = rxjs_1.scheduled(this.taskQueue, rxjs_1.asapScheduler) .pipe(operators_1.filter(function (tsk) { return tsk.status !== interface_1.StatusCode.Complete; }), operators_1.takeUntil(this.pause$)) .subscribe({ next: function (tsk) { return _this.taskSubject.next(tsk); }, complete: function () { sub_1 === null || sub_1 === void 0 ? void 0 : sub_1.unsubscribe(); sub_1 = null; }, }); } }; Uploader.prototype.taskHandlerEventCallback = function (e) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } this.emit.apply(this, tslib_1.__spread([e], args)); }; Uploader.prototype.changeUploadTaskStatus = function (task, status) { task.status = status; }; Uploader.prototype.restoreTask = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { var taskList, recoverableTaskStatus; var _this = this; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, modules_1.getStorage(this.id).UploadTask.values().toPromise()]; case 1: taskList = _a.sent(); recoverableTaskStatus = this.options.recoverableTaskStatus; if (!(taskList === null || taskList === void 0 ? void 0 : taskList.length)) { return [2 /*return*/, []]; } return [2 /*return*/, rxjs_1.scheduled(taskList || [], rxjs_1.asyncScheduler) .pipe(operators_1.filter(function (task) { if (recoverableTaskStatus === null || recoverableTaskStatus === void 0 ? void 0 : recoverableTaskStatus.length) { return recoverableTaskStatus.includes(task.status); } return true; }), operators_1.tap(function (task) { if (task.status !== interface_1.StatusCode.Complete) { task.status = task.status === interface_1.StatusCode.Error ? task.status : interface_1.StatusCode.Pause; task.progress = task.progress >= 100 ? 99 : task.progress; } _this.taskQueue.push(task); if (_this.options.autoUpload && task.status === interface_1.StatusCode.Pause) { _this.upload(task); } // 任务恢复事件 _this.emit(interface_1.EventType.TaskRestore, task); }), operators_1.last(), operators_1.mapTo(taskList), operators_1.takeUntil(this.clear$)) .toPromise() // return new Promise((resolve, reject) => { // Storage.UploadTask.list() // .then((list: unknown[]) => { // Logger.info('Uploader -> restoreTask -> list', list) // const taskList: UploadTask[] = [] // const fn = (timeRemaining?: () => number) => { // while (list.length) { // const arr = list.splice(0, 20) as UploadTask[] // arr.forEach((task) => { // if (task.status === StatusCode.Complete) { // return this.removeTaskFromStroage(task) // } // task.status = StatusCode.Pause // task.progress = task.progress >= 100 ? 99 : task.progress // this.taskQueue.push(task) // taskList.push(task) // this.options.autoUpload && this.upload(task) // // 任务恢复事件 // this.emit(EventType.TaskRestore, task) // }) // if (!timeRemaining || !timeRemaining?.()) { // break // } // } // list.length ? scheduleWork(fn) : resolve(taskList) // } // scheduleWork(fn) // }) // .catch((e) => reject(e)) // }) ]; } }); }); }; Uploader.prototype.initFilePickersAndDraggers = function () { var _this = this; var _a = this.options, filePicker = _a.filePicker, fileDragger = _a.fileDragger; var filePickers = Array.isArray(filePicker) ? filePicker : filePicker ? [filePicker] : null; var fileDraggers = Array.isArray(fileDragger) ? fileDragger : fileDragger ? [fileDragger] : null; var obs = []; if (filePickers === null || filePickers === void 0 ? void 0 : filePickers.length) { obs.push.apply(obs, tslib_1.__spread(filePickers.map(function (opts) { var picker = new modules_1.FilePicker(opts); _this.filePickers.push(picker); return picker.file$; }))); } if (fileDraggers === null || fileDraggers === void 0 ? void 0 : fileDraggers.length) { obs.push.apply(obs, tslib_1.__spread(fileDraggers.map(function (opts) { var dragger = new modules_1.FileDragger(opts, _this.options); _this.fileDraggers.push(dragger); return dragger.file$; }))); } if (obs.length) { this.subscription.add(rxjs_1.merge.apply(void 0, tslib_1.__spread(obs)).pipe(operators_1.concatMap(function (files) { return _this.add(files); })) .subscribe()); } }; Uploader.prototype.initEventHandler = function () { var _this = this; this.on(interface_1.EventType.TaskCreated, function (task) { _this.options.autoUpload && _this.upload(task); }); }; Uploader.prototype.add = function (files) { var _this = this; return rxjs_1.of(files).pipe(operators_1.concatMap(function (files) { var _a, _b; // 选择文件后添加文件前hook var beforeAdd = _this.hookWrap((_b = (_a = _this.options).beforeFilesAdd) === null || _b === void 0 ? void 0 : _b.call(_a, files)); return rxjs_1.from(beforeAdd).pipe(operators_1.mapTo(files)); }), operators_1.concatMap(function (files) { return rxjs_1.from(_this.addFilesAsync.apply(_this, tslib_1.__spread(files))).pipe(operators_1.map(function (tasks) { return ({ files: files, tasks: tasks }); })); }), operators_1.takeUntil(this.clear$)); }; Uploader.prototype.addFilesAsync = function () { var _this = this; var files = []; for (var _i = 0; _i < arguments.length; _i++) { files[_i] = arguments[_i]; } return new Promise(function (resolve, reject) { shared_1.Logger.info('Uploader -> addFile -> files', files); var resolveTasks = function (tasks) { resolve(tasks); _this.emit(interface_1.EventType.TasksAdded, tasks); }; var finish = function (tasks) { if (_this.options.resumable) { var ob$ = utils_1.isElectron() ? _this.presistTaskWithoutBlob(tasks, _this.clear$) : _this.presistTask(tasks, _this.clear$); var sub_2 = ob$.pipe(operators_1.takeUntil(_this.clear$)).subscribe({ error: function (e) { return reject(e); }, complete: function () { // 任务持久化事件 _this.emit(interface_1.EventType.TasksPresist, tasks); sub_2 === null || sub_2 === void 0 ? void 0 : sub_2.unsubscribe(); sub_2 = null; resolveTasks(tasks); console.timeEnd('addFilesAsync'); }, }); } else { resolveTasks(tasks); console.timeEnd('addFilesAsync'); } }; if (!files.length) { resolveTasks([]); return; } console.time('addFilesAsync'); var fileFilter = _this.options.fileFilter; var tasks = new Set(); rxjs_1.scheduled(files || [], rxjs_1.asapScheduler) .pipe(operators_1.filter(function (file) { var accept = true; if (fileFilter instanceof RegExp) { accept = fileFilter.test(file.name); } else if (typeof fileFilter === 'function') { accept = fileFilter(file.name, file); } return !!accept; }), operators_1.bufferCount(1000), operators_1.map(function (files) { return files.map(helpers_1.fileFactory); }), operators_1.concatMap(function (files) { return _this.generateTask.apply(_this, tslib_1.__spread(files)); }), operators_1.tap(function (data) { return data === null || data === void 0 ? void 0 : data.forEach(function (i) { return i && tasks.add(i); }); })) .subscribe({ complete: function () { return finish(tslib_1.__spread(tasks)); }, }); // const scheduleWork = (cb: Noop, timeout?: number) => cb() // const fn = async (timeRemaining?: () => number) => { // while (files.length) { // Logger.info(files.length) // const filelist: UploadFile[] = [] // files.splice(0, 100).forEach((file) => { // let ignored = false // if (fileFilter instanceof RegExp) { // ignored = !fileFilter.test(file.name) // } else if (typeof fileFilter === 'function') { // ignored = !fileFilter(file.name, file) // } // if (!ignored) { // filelist.push(fileFactory(file)) // } // }) // const currentTasks: UploadTask[] = await this.generateTask(...filelist).toPromise() // currentTasks.map((i) => tasks.add(i)) // // if (!timeRemaining?.()) { // // break // // } // } // files.length ? scheduleWork(fn, 1000) : finish([...tasks]) // } // scheduleWork(fn) }); }; Uploader.prototype.generateTask = function () { var _this = this; var fileList = []; for (var _i = 0; _i < arguments.length; _i++) { fileList[_i] = arguments[_i]; } return new rxjs_1.Observable(function (subscriber) { console.time('generateTask'); var _a = _this.options, ossOptions = _a.ossOptions, singleFileTask = _a.singleFileTask; var updateTasks = new Set(); var newTasks = []; var notifier = new rxjs_1.Subject(); var existsTaskMap = new Map(); var sub = rxjs_1.from(fileList) .pipe(operators_1.tap(function (file) { var pos = file.relativePath.indexOf('/'); var newTask = null; var inFolder = !singleFileTask && pos !== -1; if (!inFolder) { newTask = helpers_1.taskFactory(file, singleFileTask); } else { var parentPath_1 = file.relativePath.substring(0, pos + 1); var existsTask = existsTaskMap.get(parentPath_1); if (!existsTask) { existsTask = _this.taskQueue.concat(newTasks).find(function (tsk) { return tsk.fileIDList.some(function (id) { var _a; return (_a = modules_1.FileStore.get(id)) === null || _a === void 0 ? void 0 : _a.relativePath.startsWith(parentPath_1); }); }); existsTask && existsTaskMap.set(parentPath_1, existsTask); } if (existsTask) { var existsFile = existsTask.fileIDList.find(function (id) { var _a; return ((_a = modules_1.FileStore.get(id)) === null || _a === void 0 ? void 0 : _a.relativePath) === file.relativePath; }); if (!existsFile) { existsTask.fileIDList.push(file.id); existsTask.fileList.push(file); existsTask.fileSize += file.size; updateTasks.add(existsTask); } else { shared_1.Logger.info('existsTask:', existsTask, 'existsFile:', existsFile); } } else { newTask = helpers_1.taskFactory(file, singleFileTask); } } if (newTask) { newTask.oss = (ossOptions === null || ossOptions === void 0 ? void 0 : ossOptions.enable) ? ossOptions === null || ossOptions === void 0 ? void 0 : ossOptions.provider : newTask.oss; newTask.type = singleFileTask ? 'file' : newTask.type; newTasks.push(newTask); } }), operators_1.last(), operators_1.concatMap(function () { var _a, _b; return (fileList.length ? rxjs_1.from(_this.hookWrap((_b = (_a = _this.options).filesAdded) === null || _b === void 0 ? void 0 : _b.call(_a, fileList))) : rxjs_1.of(null)); }), operators_1.concatMap(function () { var _a, _b; return (newTasks.length ? rxjs_1.from(_this.hookWrap((_b = (_a = _this.options).beforeTasksAdd) === null || _b === void 0 ? void 0 : _b.call(_a, newTasks))) : rxjs_1.of(null)); }), operators_1.tap(function () { // 任务创建事件 newTasks.forEach(function (task) { _this.emit(interface_1.EventType.TaskCreated, task); _this.taskQueue.push(task); }); // 任务更新事件 updateTasks.forEach(function (task) { return _this.emit(interface_1.EventType.TaskUpdate, task); }); }), operators_1.takeUntil(notifier)) .subscribe({ complete: function () { subscriber.next(newTasks); subscriber.complete(); console.timeEnd('generateTask'); }, error: function (e) { return subscriber.error(e); }, }); return function () { notifier === null || notifier === void 0 ? void 0 : notifier.next(); notifier === null || notifier === void 0 ? void 0 : notifier.unsubscribe(); sub === null || sub === void 0 ? void 0 : sub.unsubscribe(); }; }); }; return Uploader; }(Base_1.default)); exports.Uploader = Uploader; //# sourceMappingURL=Uploader.js.map