mpa-uploader
v1.0.0
Published
## 简介
Downloads
1
Readme
MPA-UPLOAD-COMPONENTS 文件上传组件
简介
该组件负责将文件上传到云空间。整个上传文件过程,由以下 6 个阶段组成:
- 添加文件
- 计算文件的 Hash 值
- 初始化文件上传
- 文件预分块
- 上传文件
- 合并文件
名词解释:
- tid,即 Task ID, 用于标识任务的唯一 ID
- fid,即 FileId,用于标识文件的唯一 ID
上传组件提供了以下 API:
// 添加一个上传文件任务
addFiles(items, pid, behavior) { }
// 添加一个上传文件夹任务
addFolder(item, pid, behavior) { }
// 移除任务
remove(tid) { }
// 恢复任务
resume(tid) { }
// 暂停任务
async pause(tid) { }
// 销毁当前组件
destroy() { }
同时,组件在工作过程,会触发一些事件,调用方可以根据自己的需求订阅
// 新的任务,当调用 addFiles/addFolder 方法时触发
fileUploder.on("new-tasks", (tasks) => {});
// 移除任务,当调用 remove 方法时触发
fileUploder.on("remove-task", (tid) => { });
// 任务的状态发生变更,status 取值:ERROR: -1, WAITING: 0, UPLOADING: 1, AUSE: 2, COMPLETE: 3,
fileUploder.on("task-status-change", (tid, status) => { });
// 上传文件的状态发生变更 status 取值:ERROR: -1, WAITING: 0, UPLOADING: 1, AUSE: 2, COMPLETE: 3,
fileUploder.on("file-status-change", (tid, fid, status) => { });
// 上传速度,其中 e :{tid:'任务ID', rate:'上传速度(单位bytes)', commit:'当前已上传多少(单位bytes)'},
fileUploder.on("upload-speed", (e) => {});
// 上传进度,其中 e :{tid:'任务ID', uploaded:'已上传字节数', total:'任务总大小'}
fileUploder.on("upload-progress", (e) => { });
// 开始计算文件MD5
fileUploder.on("begin-hash", (tid, fid) => { });
// 计算文件MD5结束
fileUploder.on("end-hash", (tid, fid) => { });
// 计算文件MD5进度:e = { tid: '', fid: '', fileName: '文件名', percent: 进度值[0-100] }
fileUploder.on("hash-progress", (e) => { });
// 准备初始化文件
fileUploder.on("begin-prepare", (tid, fid) => { })
// 初始化文件结束
fileUploder.on("end-prepare", (tid, fid) => { });
// 初始化文件结果 file = {metadatas: '文件元数据', md5: '文件hash值', uploadId: '上传ID', blockInfo: '分块信息'}
fileUploder.on("prepared", (file) => { });
// 准备提交分块信息
fileUploder.on("begin-commit-blocks", (tid, fid) => {});
// 提交分块信息结束
fileUploder.on("end-commit-blocks", (tid, fid) => {});
// 准备上传文件
fileUploder.on("begin-upload-file", (tid, fid) => {})
// 上传文件结束
fileUploder.on("end-upload-file", (tid, fid) => {});
// 开始合并文件
fileUploder.on("begin-merge-file", (tid, fid) => {});
// 合并文件结束
fileUploder.on("end-merge-file", (tid, fid) => {});
// 上传被取消,一般调用 pause 方法时,会触发该事件
fileUploder.on("abort", (tid) => {});
// 上传完成
fileUploder.on("complete", (task) => {});
// 上传出错 e = {tid, fid, code, message}
// code 取值:
// OK: 0,
// USER_ABORT: 1,
// CALC_MD5_FAILED: 2,
// PREPARE_UPLOAD_FAILED: 3,
// COMMIT_BLOCKS_FAILED: 4,
// UPLOAD_FILE_FAILED: 5,
// MERGE_FILE_FAILED: 6,
// UNKNOWN: 7,
fileUploder.on("error", (e) => { });
初始化组件
使用组件之前,需要现对其实例化操作,实例化需要一些必须的参数,需要提供。
import FileUploader from "./FileUploader";
// 参数
const options = {
domain: "http://xxxx.com", // 接口域名
debug: true, // 是否调试模式, 将打印大量信息
headers?: {
deviceType: "1",
spaceId: "0583d",
userId: "",
token: "",
deviceId: "",
}, // 请求头信息,如果存在 cookies,则可以不用传递
};
// 实例化
const fileUploder = new FileUploader(options);
添加任务
你可以上传文件或者文件夹,添加完成后,系统将通过事件的方式,告知添加的结果,主要包含 2 个 API:
- addFiles(items, pid, behavior): 添加文件
- addFolder(item, pid, behavior): 添加文件夹
示例
<template>
<input type="file" @change="handleFileUpload" multiple />
<input type="file" @change="handleFolderUpload" webkitdirectory />
</template>
const behavior = "RENAME" // 文件重命名行为
const pid = "1111" // 要上传文件的上级文件夹 ID
const fileUploder = new FileUploader(options);
// 上传文件
handleFileUpload(event) {
const target = event.target;
const files = Array.from(target.files); // 文件列表
const ok = fileUploader.addFiles(files, pid, behavior);
if (!ok) return;// 文件添加失败
}
// 上传文件夹
handleFolderUpload(event) {
const target = event.target;
const files = Array.from(target.files);
const ok = fileUploader.addFolder(files, pid, behavior);
if (!ok) return; // 文件夹添加失败
}
// 添加文件后,创建了一个新任务的回调
fileUploder.on("new-tasks", tasks => {
// 任务列表
// 任务对象属性组成
// {
// tid: '', // 任务ID
// pid: '', // 上传文件的上级文件夹 ID
// isdir: false, // 是否是文件夹
// name: 'file.name', // 任务名称
// size: 1000, // 要上传文件(夹)的总大小 单位 byte
// behavior: 'RENAME', // 文件重名行为
// status: 0, // FileStatus 任务状态,见下列表
// }
// const FileStatus = {
// ERROR: -1, // 错误
// WAITING: 0, // 等待上传
// UPLOADING: 1, // 上传中
// PAUSE: 2, // 暂停中
// COMPLETE: 3, // 上传完成
// };
})
暂停任务
通过调用 pause,可以取消任务,一旦被取消,将触发 abort 和 task-status-change 事件,调用方可以根据自身逻辑处理渲染层。
pause(tid): 暂停任务
const fileUploder = new FileUploader(options);
fileUploder.on("abort", (tid) => {
// 任务已暂停
});
fileUploder.on("task-status-change", (tid, status) => {
// 更新页面上的任务状态
});
const tid = "929w222";
fileUploder.pause(tid);
恢复暂停或者出错的任务
通过调用 resume() 可以继续上传被暂停或者出错的任务,一旦任务开始,将触发 task-status-change 事件
const fileUploder = new FileUploader(options);
fileUploder.on("task-status-change", (tid, status) => {
// 更新页面上的任务状态
});
const tid = "929w222";
fileUploder.resume(tid);
删除任务
你可以通过 tid ,即 TaskId 删除任务,删除完成后,将通过事件的方式,通知你已删除完成。
remove(tid) : 删除任务
const fileUploder = new FileUploader(options);
fileUploder.on("remove-task", (tid) => {
// 任务已删除
});
const tid = "929w222";
fileUploder.remove(tid);
上传文件
上传文件分 5 个步骤,将依次触发下面的事件(注意,不是每个事件一定都会触发)
- 计算文件的 Hash 值,触发事件:begin-hash、hash-progress、end-hash
- 文件初始化,触发事件:begin-prepare、prepared、begin-prepare
- 文件预分块,触发事件:begin-commit-blocks、end-commit-blocks
- 文件上传,触发事件:begin-upload-file、upload-speed、upload-progress、end-upload-file
- 合并文件,触发事件:begin-merge-file、complete、end-merge-file
示例
const fileUploader = new FileUploader(options);
fileUploader.on("begin-hash" (tid, fid) => {
// 开始计算文件MD5
// tid, TaskId, 即任务 ID,随机生成的标识
// fid, FileId, 文件ID,随机生成的标识
})
fileUploader.on("end-hash" (tid, fid) => {
// 计算文件MD5完成
})
fileUploader.on("hash-progress" ({ tid, fid, fileName, percent }) => {
// 正在计算文件MD5
// fileName 正在计算的文件名
// percent 当前进度 1-100
})
fileUploader.on("complete", (task) => {
// 文件上传完成
})
VUE 示例
View
<div class="hello">
<span>文件夹</span>
<input type="file" @change="handleFolderUpload" webkitdirectory multiple />
<br />
<span>文件</span>
<input type="file" @change="handleFileUpload" multiple />
<br />
<table border="1">
<tr v-for="task of tasks" :key="task.tid">
<td>任务ID: {{ task.tid }}</td>
<td>状态:{{ task.status }}</td>
<td>速度:{{ speeder(task.tid) }}</td>
<td>进度:{{ processer(task.tid) }}</td>
<td>
<button @click="onStartTask(task.tid)">开始</button>
<button @click="onCancelTask(task.tid)">暂停</button>
<button @click="onRemoveTask(task.tid)">删除</button>
</td>
</tr>
</table>
</div>
Javascript:
import FileUploader from "mpa-upload-components";
import { bytesToSize } from "mpa-upload-components/src/utils";
const options = {
debug: true,
domain: "http://xxx.com",
headers: {
deviceType: "",
spaceId: "",
userId: "",
token: "",
deviceId: "",
},
};
const pid = "上级目录的ID";
export default {
name: "HelloWorld",
props: {
msg: String,
},
data() {
return {
fileMd5: null,
percent: 0,
tasks: [],
speeds: [],
progresses: [],
fileUploader: null,
};
},
methods: {
async handleFolderUpload(event) {
const target = event.target;
const files = Array.from(target.files);
const task = this.fileUploader.addFolder(files, pid);
},
async handleFileUpload(event) {
const target = event.target;
const files = Array.from(target.files);
const task = this.fileUploader.addFiles(files, pid);
},
onStartTask(tid) {
this.fileUploader.resume(tid);
},
onCancelTask(tid) {
this.fileUploader.pause(tid);
},
onRemoveTask(tid) {
this.fileUploader.remove(tid);
},
},
computed: {
speeder() {
return (tid) => {
const speed = this.speeds.find((t) => t.tid === tid);
if (!speed) return "";
return `${speed.rate}kb/s`;
};
},
processer() {
return (tid) => {
const progress = this.progresses.find((t) => t.tid === tid);
if (!progress) return "";
const value = ~~((progress.uploaded / progress.total) * 100);
return `${progress.uploaded}/${progress.total} (${value})%`;
};
},
},
created() {
this.fileUploader = new FileUploader(options);
this.fileUploader.on("new-tasks", (tasks) => {
this.tasks.push(...tasks);
});
this.fileUploader.on("remove-task", (tid) => {
const index = this.tasks.findIndex((task) => task.tid === tid);
this.tasks.splice(index, 1);
});
this.fileUploader.on("task-status-change", (tid, status) => {
const task = this.tasks.find((task) => task.tid === tid);
task.status = status;
});
this.fileUploader.on("upload-speed", (e) => {
const speed = this.speeds.find((t) => t.tid === e.tid);
if (speed) {
speed.rate = bytesToSize(e.rate);
speed.commit = e.commit;
} else {
this.speeds.push(e);
}
});
this.fileUploader.on("upload-progress", (e) => {
const progress = this.progresses.find((t) => t.tid === e.tid);
if (progress) {
progress.total = e.total;
progress.uploaded = e.uploaded;
} else {
this.progresses.push(e);
}
});
},
};