觉得有帮助的同学记得给个star,谢谢。Github地址文档在Web中,上传图片是一个常见的需求。在本文中,我们将介绍如何使用干净的架构模式来实现一个简单的图片上传状态管理器。CleanArchitecture是一种软件架构模式,旨在将应用程序划分为不同的层。每一层都有特定的职责和依赖关系,使整个应用程序更易于维护和测试。现在,让我们看看这个图片上传状态管理器的实现。需求分析业务场景:需求A:管理后台需要上传一张图片,大小限制为5m,然后发送给oss,获取返回信息,并将返回信息保存到服务器。需求B:小程序需要上传一张限制大小5m的图片,发送给oss,获取返回信息,并将返回信息保存到服务器需求C:app中的H5需要上传一张图片,限制大小为2m,宽高100px,发送给七牛云,获取返回信息,将返回信息保存到服务器需求D:...从上面的场景来看,这些需求都是类似的,整体流程基本不变,即可以抽象成下图。根据这个流程图,可以得到Presenter的接口,如下,然后把图片选择功能做成一个界面,再把上传功能做成一个界面。当我们需要切换功能时,我们可以替换具体的实现。基于以上流程,我们抽象出一个图片选择和上传业务组件状态。首先,让我们来定义视图所需要的状态,根据我们的使用场景,我们可以定义如下状态导出类型IFile={file:File|Wx文件路径;//上传的源文件thumbUrl:string;//缩略图地址id:string;//唯一id,自动生成url:string;名称:字符串;//上传文件名,由上传函数提供status:'default'|'待定'|'失败'|'成功'|'中止';};导出类型IFileList=IFile[];接口IViewState{loading:boolean;fileList:IFileList;}Presenter类实现接下来我们来实现Presenter类。用于与视图层进行交互,提供viewstate和对应的方法完整代码如下:可以看到我们的Presenter向视图层提供了以下方法:使用showLoading开关loadinghideLoadingselectselectimageuploaduploadimageremoveremoveselectedimageselectAndUploadselect和uploadreplaceAt替换对应的图片@injectable()exportclassUploadImagePresenterextendsPresenter{constructor(@inject(SelectImageServiceToken)privateselectImageService:AbsSelectImageService,@inject(UploadServiceToken)privateuploadService:AbsUploadService,){super();this.state={loading:false,fileList:[]};}showLoading(){this.setState((s)=>{s.loading=true;});}hideLoading(){this.setState((s)=>{s.loading=false;});}/***调用图片选择服务添加或替换文件列表*@returns*/select(index?:number){if(index!==undefined){if(this.state.fileList[index]){//图片选择服务允许返回多个文件//如果指定下标,则只选择第一个文件returnthis.selectImageService.__selectAndRunMiddleware().then((文件)=>{this.setState((s)=>{s.fileList[索引].file=文件[0];//重置状态s.fileList[index].status='default';});});}throwError(`index:(${index})notfoundinfileList`);}else{//图片选择方法returnthis.selectImageService.__selectAndRunMiddleware().then((files)=>{this.setState((s)=>{s.fileList=[...s.fileList,...files.map((v)=>makeFile(v))];});});}//}/***调用上传服务上传指定文件更新fileList状态*指定后面选择对应的文件,否则选择最后一个文件*@paramindex*@returns*/upload(index?:number){consti=typeofindex==='number'?索引:this.state.fileList。长度-1;constfile=this.state.fileList[i];if(!file){throwError(`index:${index}uploadFileoutofindex,`);}if(file.status!=='successful'){this.showLoading();returnthis.uploadService.upload(file.file).then((res)=>{this.setState((s)=>{s.fileList[i].url=res.url;s.fileList[i].name=res.name;s.fileList[i].thumbUrl=res.thumbUrl;s.fileList[i].status='成功';});}).catch((e)=>{this.setState((s)=>{s.fileList[i].status='failed';});throwe;}).finally(()=>{this.hideLoading();});}//文件上传成功returnPromise.resolve(true);}/***移除文件*@paramindex*/remove(index:number){this.setState((s)=>{s.fileList.splice(index,1);});}/***选择一张图片并上传最后一张*/asyncselectAndUpload(){awaitthis.select();awaitthis.upload();}replaceAt(index:number,file:IFile){this.setState((s)=>{s.fileList[index]={...s.fileList[index],...file,};});}}在UploadImagePresenter类中,我们依赖两个抽象服务类:AbsSelectImageService和AbsUploadService,通过依赖注入来替代真正的图片选择服务和上传服务。实现了视图、状态、服务等几个层次的解耦。比如我们的选图服务可以是“浏览器选图”或者“微信小程序选图”。上传服务可以上传到阿里云oss,或者七牛或者自己的服务器等图片选择服务import{IMiddleware,MiddlewareRunner}from'@lujs/middleware';exporttypeWxFilePath=string;exporttypeSelectRes=(File|WxFilePath)[];/***微信图片选择返回图片路径*/interfaceISelect{():Promise;}/***基础图片选择服务*/exportabstractclassAbsSelectImageService{abstractselect():Promise<选择结果>;middlewareRunner=newMiddlewareRunner();__selectAndRunMiddleware(){返回this.select().then((fs)=>this.middlewareRunner.run(fs));}useMiddleware(middleware:IMiddleware){this.middlewareRunner.use(middleware);}}/***图片选择函数生成器*/interfaceBrowserInputSelect{//接受的参数accept?:string;捕获?:布尔值;multiple?:boolean;}exportclassSelectFnFactor{staticbuildBrowserInputSelect(option?:BrowserInputSelect){constDefaultBrowserInputSelect={accept:'image/*',捕获e:false,multiple:false,};constopt={...DefaultBrowserInputSelect,...选项,};return()=>{让isChoosing=false;returnnewPromise((resolve,reject)=>{const$input=document.createElement('input');$input.setAttribute('id','useInputFile');$input.setAttribute('type','file');$input.style.cssText='opacity:0;position:absolute;top:-100px;left:-100px;';$input.setAttribute('accept',opt.accept);document.body.appendChild($input);constunMount=()=>{//eslint-disable-next-lineno-use-before-define$input.removeEventListener('change',changeHandler);document.body.removeChild($input);};constchangeHandler=()=>{isChoosing=false;if($input.files){constfs=[...$input.files];unMount();resolve(fs);}}//允许重复选择一个文件$input.value='';};$input.addEventListener('change',changeHandler);//取消选择文件window.addEventListener('focus',()=>{setTimeout(()=>{if(!isChoosing&&$input){unMount();reject(newError('onblur'));}},300);},{一次:真},);$input.click();是选择=真;});};}/***微信小程序选择*/staticbuildWxMPSelect(option:{count?:number;sourceType?:('album'|'camera')[];}={},):ISelect{return()=>{//eslint-disable-next-lineno-undefif(wx===undefined){throwError('wxisundefined');}else{//eslint-disable-next-lineno-undefreturnwx.chooseMedia({...option,mediaType:['image'],}).then((value)=>value.tempFiles.map((v)=>v.tempFilePath));}};}}constbrowserInputSelect:ISelect=SelectFnFactor.buildBrowserInputSelect({accept:'image/*',});/***浏览器输入选择服务*/exportclassSelectImageServiceBrowserInputextendsAbsSelectImageService{select=()=>browserInputSelect();}选择中间件我们可以通过内置的中间件等方式来限制图片的尺寸大小exportclassMySelectImageServiceextendsAbsSelectImageService{//选择图片中间件,检查图片大小this.useMiddleware(SelectImageMiddlewareFactor.checkSize({max:100*1024}),);//最大限制为100k}select(){//自定义图片选择函数,使用浏览器的输入来选择图片constbrowserInputSelect=SelectFnFactor.buildBrowserInputSelect({accept:'image/*',});返回浏览器输入选择();}}uploadserviceexportabstractclassAbsUploadService{abstractupload(files:File|WxFilePath):Promise;}觉得有帮助记得给个star,感谢github地址