fairy-webuploader
Version:
220 lines (210 loc) • 7.29 kB
JavaScript
const defaultEl = '#webUploader';
import SparkMD5 from './lib/spark-md5.js'
import Template from './template/index.js'
import UploadUtil from './UploadUtil.js';
/**
* 大文件分片上传
*/
export default class WebUploader {
constructor(options) {
this.options = options;
this.el = options?.el || defaultEl;
this.uploadCallBack = options?.uploadCallBack;
this.allSuccessCallback = options?.allSuccessCallback;
this.chunkSize = options?.chunkSize || 5 * 1024 * 1024; // 5M
this.fileList = []
// 初始化上传网络请求
this.UploadUtil = new UploadUtil({
BASE_URL: options?.BASE_URL,
headers: options?.headers,
});
this.multiple = options?.multiple || true;
this.accept = options?.accept || '*';
// 获取并初始化上传模板
this.templateName = options?.template;
if(this.templateName){
this.initTemplate()
}
// 进度处理方法列表
this.progressHandlers = [];
}
initTemplate () {
this.template = Template.getTemplate(this.templateName)
this.templateInstence = new this.template(this.el,{
handleFileSelect: this.inputChange.bind(this),
preview: this.options?.preview,
multiple: this.multiple,
accept: this.accept,
});
}
/**
* 选择文件比变化
* @param files 文件对象[]
*/
inputChange(files){
this.fileList.push(...files);
this.uploadHandle()
}
/**
* 添加进度监听器
* @param handler
* @returns {WebUploader}
*/
onProgress(handler) {
this.progressHandlers.push(handler);
return this;
}
processHandler(fileIndex,progress){
this.fileList[fileIndex].progress = progress
this.templateInstence.updateProgress(this.fileList[fileIndex].uuid, progress)
}
/**
* 触发进度更新
* @param fileIndex 文件索引
* @param totalChunks 总分片
* @param uploadedChunks 已上传分片
*/
updateProgress(fileIndex,totalChunks,uploadedChunks) {
const progress = (uploadedChunks / totalChunks) * 100;
this.progressHandlers.forEach(handler => handler(fileIndex,progress));
}
/**
* 选择文件
* @param file
*/
setFileList(...file){
this.fileList.push(...file);
}
/**
* 单个文件上传成功回调
* @param frFile
*/
uploadSuccess(frFile){
this.uploadCallBack?.(frFile)
}
/**
* 全部文件上传成功
* @param fileList
*/
uploadSuccessAll(fileList){
this.templateInstence.clearInput()
this.allSuccessCallback?.(fileList)
this.fileList = []
}
/**
* 上传文件
*/
uploadHandle(){
let uploadIndex = 0;
const fileList = []
const updateUploadIndex = () => {
uploadIndex++
if (uploadIndex === this.fileList.length) {
this.uploadSuccessAll(fileList)
}
}
this.fileList.forEach((file,fileIndex) => {
this.getFileMd5(file).then(async MD5 => {
this.onProgress(this.processHandler.bind(this))
let exits = await this.UploadUtil.queryBigFile(MD5)
exits = JSON.parse(exits)
if (exits && exits.length > 0) {
this.buildFileList(fileIndex,exits[0])
fileList.push(exits[0])
this.uploadSuccess(exits[0])
this.updateProgress(uploadIndex,1,1)
updateUploadIndex()
return
}
const chunks= await this.createFileChunk(file)
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i]
const chunkMd5 = chunk.chunkMd5
const fd = new FormData()
fd.append('file', chunk.file)
fd.append('fileName', file.name)
fd.append('chunkMd5', `${chunkMd5}`)
fd.append('md5',`${MD5}`)
fd.append('chunk',`${i}`)
const result = await this.UploadUtil.queryFile({
md5: MD5,
chunkMd5,
chunk: i
})
if (result === 'false'){
await this.UploadUtil.upload(fd)
this.updateProgress(uploadIndex,chunks.length,i)
}
if (i === chunks.length - 1 ) {
this.UploadUtil.mergeFile({
fileName: file.name,
md5: MD5,
}).then(res => {
this.buildFileList(fileIndex,res)
fileList.push(res)
this.updateProgress(uploadIndex,chunks.length,chunks.length)
this.uploadSuccess(res)
updateUploadIndex()
})
}
}
})
})
}
/**
* 文件分片
* @param file 文件对象
* @returns {Promise<[]>}
*/
async createFileChunk (file) {
const fileSize = file.size
const chunks = []
let start = 0;
const st = new Date().getMilliseconds()
while (start < file.size) {
const end = Math.min(start + this.chunkSize, file.size);
const chunk = file.slice(start, end);
const chunkMd5 = await this.getFileMd5(chunk)
console.log(`第${chunks.length + 1}分片解析完成, MD5值为${chunkMd5}`);
chunks.push({
file:chunk,
fileSize,
chunkMd5,
size: Math.min(this.chunkSize, fileSize)
});
start = end;
}
const end = new Date().getMilliseconds()
console.log(`文件分片完成,共分${chunks.length}片,用时${end - st}ms`);
return chunks;
}
/**
* 计算文件Md5
* @param file 文件对象
* @returns {Promise<unknown>}
*/
getFileMd5(file){
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(file)
const spark = new SparkMD5.ArrayBuffer()
fileReader.onload = function (e){
spark.append(e.target.result);
const md5 = spark.end()
spark.destroy()
resolve(md5)
}
fileReader.onerror = function (e){
reject(e)
}
})
}
/**
* 构建文件结果
* @param fileIndex
* @param result
*/
buildFileList(fileIndex,result) {
this.fileList[fileIndex].response = result;
}
}