UNPKG

instruct-request-axios

Version:
627 lines (517 loc) 22.6 kB
import { RequestConfigInstruction, InstructionPostOption, ResponseData, RequestResponse, DefaultRequestConfigInstruction } from '../../type'; import { RequestUploadInstructionFile, RequestUploadParams, RequestContxtParams, CacheStorageType, RequestUploadCache } from './type'; import Request from '../../request'; import PromiseExtend from "../../../extend/ProsmiseExtend"; import SparkMD5 from 'spark-md5'; import Cache from '../cache/cache'; import UploadContxt from './slice/uploadContxt'; import UploadExtend from './slice/uploadExtend'; export default class UploadSlice<T,I,D> extends UploadExtend{ constructor(private request:Request<T,D>,cache:Cache) { super(cache); } // 是否有可以执行 static jurisdiction:boolean = !!(FileReader && Blob); // @ts-ignore blobSlice=File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; // 触发upload triggerUpload<childT = T,childD = D>(config:InstructionPostOption,requestConfig:I & RequestConfigInstruction<childT,I,childD>,option?:RequestUploadInstructionFile<any>,uploadContxt?:UploadContxt){ if(!uploadContxt || !uploadContxt.storageContent || !uploadContxt.unique) { // @ts-ignore let fileOption:RequestUploadInstructionFile<T,I,D> = Object.assign({},option,requestConfig.file); let resultUploadContxt = uploadContxt || fileOption.controller; if(resultUploadContxt && resultUploadContxt.__unique) return; let uploadConfig:RequestUploadParams = this.createConfig(fileOption); if(fileOption.merge && fileOption.mergeAnalysis) { uploadConfig.__merge = UploadSlice.getAddSpeedNumber(fileOption.mergeAnalysis,uploadConfig.total,fileOption); } if(fileOption.controller) { fileOption.controller.setParams({ storageContent:{ requestConfig, config, uploadConfig, fileOption, option } }) }; if(resultUploadContxt) { resultUploadContxt.__unique = true; } this.unique(fileOption,uploadConfig,config,resultUploadContxt).then((hash)=>{ if(resultUploadContxt) { resultUploadContxt.__unique = false; } return this.triggerUnique({ hash, config, requestConfig, uploadConfig, fileOption }); }).catch((response)=>{ if(resultUploadContxt) { resultUploadContxt.__unique = false; } config.responseData = response; config.status = "fail"; return this.request.triggerPost(config,function (){ config = null; }); }); } else { let { fileOption,uploadConfig } = uploadContxt.storageContent; return this.triggerUnique({ hash: uploadContxt.unique, config, requestConfig, uploadConfig, fileOption, uploadContxt }); } } // 触发 triggerUnique<childT = T,childD = D>({ hash, config, requestConfig, uploadConfig, fileOption, uploadContxt }:{ hash:string, config:InstructionPostOption, requestConfig:I & RequestConfigInstruction<childT,I,childD>, uploadConfig:RequestUploadParams, fileOption?:RequestUploadInstructionFile<T,I,D>, uploadContxt?:UploadContxt }){ uploadConfig.hash = hash; let onUploadProgress = config.requestData.onUploadProgress; // 如果不存在uploadContxt 触发 if(!uploadContxt || !uploadContxt.storageContent || !uploadContxt.surplus) { uploadContxt = uploadContxt || fileOption.controller; let cache = this.getCacheParams(fileOption.cache); let storageContxt:RequestContxtParams; if(fileOption.cache && fileOption.record) { let resutlStorageContxt = this.cache.getItem(hash,cache); if(resutlStorageContxt && resutlStorageContxt.data) { storageContxt = resutlStorageContxt.data as unknown as RequestContxtParams; } } // 创建基本配置 let contxt:RequestContxtParams = { unique:hash, storage: fileOption.storage, analysis:uploadConfig.__analysis, mergeAnalysis: uploadConfig.__merge, total: uploadConfig.total, cache, success:undefined, name: uploadConfig.name, surplus:undefined, storageContent:{ requestConfig, config, uploadConfig, fileOption } } if(storageContxt) { let surplus = storageContxt.surplus; if(storageContxt.fail){ surplus.push(...storageContxt.fail); } contxt.success = storageContxt.success; contxt.surplus = surplus; contxt.name = uploadConfig.name || storageContxt.name; if(uploadContxt) { uploadContxt.setParams(contxt); } else { uploadContxt = new UploadContxt(contxt); } } else { let surplus = []; for(let i=uploadConfig.total - 1;i>=0;i--) { surplus.push(i) } contxt.surplus=surplus; if(uploadContxt) { uploadContxt.setParams(contxt); } else { uploadContxt = new UploadContxt(contxt); } uploadContxt.setStorage(hash,this.cache); } } if(uploadContxt.suspend) return; if(config && onUploadProgress) { UploadSlice.onUploadProgress(undefined,uploadConfig,uploadContxt,onUploadProgress); } // 根据模式获取开启管道数量 let number = 0; switch(fileOption.mode){ case 'all': number = uploadConfig.total;break; case 'many': number = fileOption.manyNumber || 1;break; default: number = 1;break; } if(uploadContxt.surplus.length < number) { number = uploadContxt.surplus.length; } const callback = (response)=> { // 如果为暂停 停止执行此操作 if(uploadContxt.suspend || uploadContxt.exit) return; // 触发完成 if(config && onUploadProgress) { UploadSlice.onUploadProgress(undefined,uploadConfig,uploadContxt,onUploadProgress); } if(fileOption.merge){ let object = this.getRequestObject(fileOption,response,uploadConfig); object.cancelToken = uploadContxt.getCancelToekn(); let rule = !!(uploadConfig.__merge && onUploadProgress); if(rule) { let toSotrageOnUploadProgress = object.onUploadProgress; object.onUploadProgress = function(progress){ UploadSlice.mergeUploadProgress(progress,uploadConfig,onUploadProgress); return toSotrageOnUploadProgress && toSotrageOnUploadProgress(progress); }; } this.request['$'+(object.requestMode || 'request')](object).then((response)=>{ if(uploadContxt.exit) return; // 如果可以执行 rule && UploadSlice.mergeUploadProgress(undefined,uploadConfig,onUploadProgress,true); // 删除暂存 uploadContxt.removeStorage(uploadConfig.hash,this.cache); return this.request.setSuccessResponseData(response,config); }).catch((response)=>{ config.responseData = response; config.status = "fail"; }).finally(()=>{ uploadContxt.clear(); return this.request.triggerPost(config,function (){ config = null; }); });; } else { // 删除暂存 uploadContxt.removeStorage(uploadConfig.hash,this.cache); this.request.setSuccessResponseData(response,config); uploadContxt.clear(); return this.request.triggerPost(config,function (){ config = null; }); } }; if(number > 0) { if(uploadContxt.running) { Object.keys(uploadContxt.running).map((item)=>{ if(uploadContxt.running[item] && uploadContxt.running[item]<1) { number -=1; } }); } // 执行管道 for(let i=0;i<number;i++) { this.queue({ fileOption, uploadContxt, config, uploadConfig, callback }); } } else { return callback && callback({}); } } upload<childT = T,childD = D>(requestConfig:I & RequestConfigInstruction<childT,I,childD>,option?:RequestUploadInstructionFile<any>):PromiseExtend<childT,childD> { let promiseExtend = new PromiseExtend<childT,childD>((resolve,reject)=>{ if(!UploadSlice.jurisdiction){ requestConfig.file.support && requestConfig.file.support(requestConfig); } if(requestConfig.file && requestConfig.file.controller){ // 清除状态 requestConfig.file.controller.clearDefault(); } // 创建配置文件 let config = this.request.createFront(requestConfig,{ "success":resolve, "fail":reject }) as InstructionPostOption; if(config) { return this.triggerUpload<childT,childD>(config,requestConfig,option); } else { promiseExtend = null; config = null; } }); return promiseExtend; } // 合并上传进度控制 static mergeUploadProgress(progress,uploadConfig:RequestUploadParams,onUploadProgress,success?:boolean){ let speed = 0; if(progress){ let speed = progress.loaded / progress.total; speed = speed > 1 ? 1 : speed; } return onUploadProgress(UploadExtend.getSpeedParams({ loaded: uploadConfig.total + (uploadConfig.__merge * speed), total:uploadConfig.total, analysis:true, mergeAnalysis:success },uploadConfig)); } // 获取 protected getCacheParams(cache:CacheStorageType | RequestUploadCache):RequestUploadCache{ if(typeof cache === 'string') { return { storage: cache }; } else { return cache; } } // 请求 merge protected getRequestObject(fileOption,response,uploadConfig){ if(typeof fileOption.merge === 'function') { return fileOption.merge(response,uploadConfig); } else { // 合并data let replaceData = fileOption.merge.replaceData || fileOption.replaceData; if(replaceData) { fileOption.merge.data = Object.assign({},fileOption.merge.data,this.repleace({},replaceData,uploadConfig,['file'])); } return fileOption.merge; } } // 执行队列 protected queue({ uploadContxt, fileOption, config, uploadConfig, callback }:{ fileOption:RequestUploadInstructionFile<T,I,D>, uploadContxt:UploadContxt, config:InstructionPostOption, uploadConfig:RequestUploadParams, callback:(response)=>void }){ // 如果处于暂停状态,暂停执行 if(uploadContxt.suspend || uploadContxt.end) return; let index = uploadContxt.getNextSlice(); if(index === undefined) return; // 创建使用配置 let resultUploadConfig:RequestUploadParams ={ ...uploadConfig, file: this.slice(fileOption,index,uploadConfig), index } if(uploadContxt.running === undefined) { uploadContxt.running = {}; } // 注入进入running状态 uploadContxt.running[index] = 0; // 合并data let replaceData = fileOption.replaceData; if(replaceData) { config.requestData.data = this.repleace(config.introduces.data,replaceData,resultUploadConfig); } let onUploadProgress = config.requestData.onUploadProgress; let useUploadProgress; if(onUploadProgress) { useUploadProgress = (params)=>{ return UploadSlice.onUploadProgress(params,resultUploadConfig,uploadContxt,onUploadProgress); }; } let mergeRequestData:DefaultRequestConfigInstruction = {rest:false,onUploadProgress:useUploadProgress,cancelToken:uploadContxt.getCancelToekn()}; if(!UploadSlice.jurisdiction) { if(config.requestData.file.supportURL) { mergeRequestData.url = config.requestData.file.supportURL; } } return this.request.upload<ResponseData,ResponseData>(Object.assign({},config.requestData,mergeRequestData)).then((response)=>{ // 如果仍然存在分片触发 if(this.request.verificationSuccessful(response,fileOption,config)) { uploadContxt.setSuccessSlice(resultUploadConfig.index); if(uploadContxt._surplus.length > 0){ return this.queue({ uploadContxt, fileOption, config, uploadConfig, callback }); }else { // 如果不处于暂停,执行暂停 if(!uploadContxt.end) { uploadContxt.over(); } if(uploadContxt.end && uploadContxt.surplus.length > 0) { return; } else { return callback && callback(response); } } } else { uploadContxt.addFail(resultUploadConfig.index); this.request.setSuccessResponseData(response,config); return this.request.triggerPost(config,function (){ config = null; }); } }).catch((response)=>{ uploadContxt.addFail(resultUploadConfig.index); config.responseData = response; config.status = "fail"; return this.request.triggerPost(config,function (){ config = null; }); }).finally(()=>{ if(!uploadContxt.end) { // 执行记录 return uploadContxt.setStorage(resultUploadConfig.hash,this.cache); } }); } // 上传进度控制 static onUploadProgress(progress,resultUploadConfig:RequestUploadParams,uploadContxt:UploadContxt,onUploadProgress){ return setTimeout(function(){ if(progress && uploadContxt.running[resultUploadConfig.index] !== null){ // 获取当前进度 if(!uploadContxt.success || !uploadContxt.success[resultUploadConfig.index]) { let speed = progress.loaded / progress.total; speed = speed > 1 ? 1 : speed; uploadContxt.running[resultUploadConfig.index] = speed; } } // 如果处于暂停状态停止执行 if(uploadContxt.suspend || uploadContxt.end) return; let loaded; if(uploadContxt.running) { loaded = Object.keys(uploadContxt.running).reduce((value,key)=> { if(uploadContxt.running[key] !== null && (!uploadContxt.success || !uploadContxt.success[key])) { return (uploadContxt.running[key] || 0) + value } return value; },uploadContxt.success ? uploadContxt.success.length : 0); } else { loaded = uploadContxt.success.length; } return onUploadProgress(UploadExtend.getSpeedParams({ loaded: loaded > resultUploadConfig.total ? resultUploadConfig.total : loaded, total:resultUploadConfig.total, analysis:true },resultUploadConfig)); }); } // 替换 repleace(data:Record<string,any>={},replaceData:{ [key in string]: keyof RequestUploadParams },resultUploadConfig:RequestUploadParams,noIncludes?:Array<string>){ for(let key in replaceData) { if(replaceData.hasOwnProperty(key)) { if(!noIncludes || !noIncludes.includes(key)) { data[key] = resultUploadConfig[replaceData[key]] } } } return data; } // 创建配置 createConfig(fileOption:RequestUploadInstructionFile<T,I,D>):RequestUploadParams{ return { index:0, total: UploadSlice.jurisdiction ? Math.ceil(fileOption.file.size / fileOption.splitSize) : 1, size: fileOption.file.size, file: fileOption.file, name: fileOption.name || (fileOption.file as File).name, hash:'' }; } // 获取增加 % 数量 static getAddSpeedNumber(number:number,total:number,fileOption:any){ let speed = 100 - (fileOption.analysis || 0) - (fileOption.mergeAnalysis || 0); speed = speed < 0 ? 0 : speed; return total / speed * number; } // 获取唯一值 unique(fileOption:RequestUploadInstructionFile<T,I,D>,config:RequestUploadParams,requestConfig?:InstructionPostOption,uploadContxt?:UploadContxt):Promise<string>{ return new Promise<string>((relove,reject)=> { if(fileOption.analysis) { config.__analysis = UploadSlice.getAddSpeedNumber(fileOption.analysis,config.total,fileOption); } if(fileOption.unique) { if((uploadContxt && !uploadContxt.suspend) && requestConfig && requestConfig.requestData.onUploadProgress && config.__analysis) { requestConfig.requestData.onUploadProgress(UploadExtend.getSpeedParams({ loaded: config.__analysis, total: config.total + config.__analysis, },config)); } if(uploadContxt) { uploadContxt.setParams({unique:fileOption.unique}); } return relove(fileOption.unique); } else { if(UploadSlice.jurisdiction) { let fileReader = new FileReader(); let spark = new SparkMD5.ArrayBuffer(); let currentChunk = config.index; fileReader.onload = function (e) { spark.append(e.target.result); currentChunk++; if((uploadContxt && !uploadContxt.suspend) && requestConfig && requestConfig.requestData.onUploadProgress && config.__analysis) { requestConfig.requestData.onUploadProgress(UploadExtend.getSpeedParams({ loaded: config.__analysis * (currentChunk / config.total), total: config.total + config.__analysis },config)); } if (currentChunk < config.total) { return loadNext(); } else { let hash = spark.end(); if(uploadContxt) { uploadContxt.setParams({unique:hash}); } return relove(hash); } }; fileReader.onerror = reject; const loadNext =()=>{ return fileReader.readAsArrayBuffer(this.slice(fileOption,currentChunk,config)); } loadNext(); } else { let dateNumber = +new Date(); if(this.distribution === undefined){ this.distribution = dateNumber+'-'+Math.ceil(Math.random() * 10000000); } return relove(config.name + '-' + dateNumber + '-' + Math.ceil(Math.random() * 10000000)+'-'+this.distribution); } } }) } private distribution:string; slice(fileOption,index:number,config:RequestUploadParams){ if(UploadSlice.jurisdiction) { let start = index * fileOption.splitSize, end = ((start + fileOption.splitSize) >= config.size) ? config.size : start + fileOption.splitSize; return this.blobSlice.call(fileOption.file,start,end); } else { return fileOption.file; } } }