UNPKG

fairy-webuploader

Version:

220 lines (210 loc) 7.29 kB
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; } }