instruct-request-axios
Version:
627 lines (517 loc) • 22.6 kB
text/typescript
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;
}
}
}