UNPKG

ks3-js-sdk

Version:

js sdk for ks3

260 lines (236 loc) 8.16 kB
var utils = require('./utils'); var session = require('./session'); var UploadQueueSize = 10000 // 上传队列最大数量,超过该数量会自动清除已完成/暂停/已取消的任务,暂不开放给用户 var originApiMap = {}; var transferToTaskMethod = function (apiMap, apiName) { originApiMap[apiName] = apiMap[apiName]; // 每次调用 putUploadFile 和 sliceUploadFile 实际执行了一遍 this._addTask apiMap[apiName] = function (params, callback) { this._addTask(apiName, params, callback); }; }; var initTask = function (ks3) { var queue = []; var tasks = {}; var uploadingFileCount = 0; var nextUploadIndex = 0; // 接口返回简略的任务信息 var formatTask = function (task) { var t = { taskId: task.taskId, bucket: task.bucket, region: task.region, key: task.key, filePath: task.filePath, state: task.state, // checking uploading waiting paused canceled error success loaded: task.loaded, size: task.size, speed: task.speed, percent: task.percent, statusCode: task.statusCode, error: task.error, }; if (task.FilePath) t.FilePath = task.FilePath; return t; }; // 任务列表更新事件 var emitListUpdate = (function () { var timer; var emit = function () { timer = 0; ks3.emit('list-update', { list: queue }); }; return function () { // 如果没有 timer 就创建一个 // 避免在短时间多次调用 emitListUpdate if (!timer) timer = setTimeout(emit); }; })(); // 清理任务队列中已完成,取消或不活跃的任务 var clearQueue = function () { if (queue.length <= UploadQueueSize) return; // 小于最大限制,无需清理 for ( var i = 0; i < nextUploadIndex && // 清理当前处理到的任务索引之前的部分 i < queue.length && queue.length > UploadQueueSize; // 队列长度超过最大限制 ) { var isActive = queue[i].state === 'waiting' || queue[i].state === 'checking' || queue[i].state === 'uploading'; if (!queue[i] || !isActive) { tasks[queue[i].taskId] && delete tasks[queue[i].taskId]; // 从 tasks map 中删除 queue.splice(i, 1); // 从 queue 中移除 nextUploadIndex--; // 下一个索引任务减一 } else { i++; } } emitListUpdate(); // 触发更新 }; // 任务调度:从队列中取出下一个待执行的任务,并启动 var startNextTask = function () { // 检查是否允许增加执行进程,是否达到并发限制 if (uploadingFileCount >= ks3.config.fileParallelLimit) return; // 跳过不可执行的任务 while (queue[nextUploadIndex] && queue[nextUploadIndex].state !== 'waiting') nextUploadIndex++; // 检查是否已遍历结束 if (nextUploadIndex >= queue.length) return; // 上传该遍历到的任务 var task = queue[nextUploadIndex]; nextUploadIndex++; uploadingFileCount++; task.state = 'checking'; !task.params.UploadData && (task.params.UploadData = {}); var apiParams = utils.extend({}, task.params); originApiMap[task.api].call(ks3, apiParams, function (err, data) { if (!ks3._isRunningTask(task.taskId)) return; if (!data) { // 网络错误 task.statusCode = 0; } else { data ? task.statusCode = data.statusCode : 500; } if (task.state === 'checking' || task.state === 'uploading') { task.state = err ? 'error' : 'success'; err && (task.error = err); uploadingFileCount--; emitListUpdate(); startNextTask(); task.callback && task.callback(err, data); if (task.state === 'success') { if (task.params) { delete task.params.UploadData; delete task.params.file; delete task.params; } delete task.callback; } } clearQueue(); }); emitListUpdate(); // 异步执行下一个任务 setTimeout(startNextTask); }; var killTask = function (id, switchToState) { var task = tasks[id]; if (!task) return; var waiting = task && task.state === 'waiting'; var running = task && (task.state === 'checking' || task.state === 'uploading'); // 检查中或者上传中视为 running if ( (switchToState === 'canceled' && task.state !== 'canceled') || (switchToState === 'paused' && waiting) || (switchToState === 'paused' && running) ) { // 如果是要变成取消或者暂停状态 if (switchToState === 'paused' && task.params.Body && typeof task.params.Body.pipe === 'function') { console.error('stream not support pause'); return; } task.state = switchToState; ks3.emit('inner-kill-task', { taskId: id, toState: switchToState }); try { var UploadId = task && task.params && task.params.UploadData.UploadId; } catch (e) {} if (switchToState === 'canceled' && UploadId) session.removeUsing(UploadId); // 如果是取消,从 session 中移除 emitListUpdate(); if (running) { uploadingFileCount--; startNextTask(); } if (switchToState === 'canceled') { if (task.params) { delete task.params.UploadData; delete task.params.file; delete task.params; } delete task.callback; } } clearQueue(); }; // 给 ks3 实例增加任务方法 ks3._addTasks = function (taskList) { utils.each(taskList, function (task) { ks3._addTask(task.api, task.params, task.callback, true); }); emitListUpdate(); }; ks3._addTask = function (api, params, callback, ignoreAddEvent) { // 复制参数对象 params = utils.extend({}, params); // 生成随机id var id = utils.uuid(); params.taskId = id; params.onTaskReady && params.onTaskReady(id); var task = { params: params, callback: callback, api: api, index: queue.length, taskId: id, bucket: params.bucket, region: params.region, key: params.key, state: 'waiting', // 状态:等待 loaded: 0, // 加载大小 size: 0, speed: 0, // 速度 percent: 0, // 进度 error: null, statusCode: null }; var onProgress = params.onProgress; params.onProgress = function (info) { if (!ks3._isRunningTask(task.taskId)) return; task.state === 'checking' && (task.state = 'uploading'); // 将 checking 状态改为 uploading task.loaded = info.loaded; task.speed = info.speed; task.percent = info.percent; onProgress && onProgress(info); emitListUpdate(); }; // 异步获取 filesize utils.getFileSize(api, params, function (err, size) { // 开始处理上传 if (err) return callback(err); // 如果获取大小出错,不加入队列 // 获取完文件大小再把任务加入队列 tasks[id] = task; queue.push(task); task.size = size; emitListUpdate() startNextTask(); }); return id; }; // 是否是正在运行的任务 ks3._isRunningTask = function (id) { var task = tasks[id]; return !!(task && (task.state === 'checking' || task.state === 'uploading')); }; // 获取任务列表 ks3.getTaskList = function () { return utils.map(queue, formatTask); }; // 取消任务 ks3.cancelTask = function (id) { killTask(id, 'canceled'); }; // 暂停任务 ks3.pauseTask = function (id) { killTask(id, 'paused'); }; // 重试任务 ks3.restartTask = function (id) { var task = tasks[id]; if (task && (task.state === 'paused' || task.state === 'error')) { task.state = 'waiting'; emitListUpdate(); nextUploadIndex = Math.min(nextUploadIndex, task.index); startNextTask(); } }; ks3.isUploadRunning = function () { return uploadingFileCount || nextUploadIndex < queue.length; }; }; module.exports.transferToTaskMethod = transferToTaskMethod; module.exports.init = initTask;