ks3-js-sdk
Version:
js sdk for ks3
260 lines (236 loc) • 8.16 kB
JavaScript
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;