在我们很多模块里面,都需要使用到一些诸如图片、Excel文件、PDF文件等附件的管理,一般我们倾向于把它独立为一个公用的附件管理模块,这样可以有效的统一管理附件的信息。本篇随笔介绍附件内容的管理,包括可以对图片进行预览,对其他文件实现信息的查看和下载等操作,以及后端提供对应的附件上传等处理。
1、附件管理的界面
和其他模块一样,我们可以对附件记录表里面的信息进行管理,一般情况下,我们管理的附件都是诸如图片、Excel文件、PDF文件等附件的管理。
附件表是一个综合管理这些文件的记录表,虽然附件一般是独立上传到服务器端的文件系统里面,不过也需要记录这些文件的名称、类别名称、大小、后缀名、创建时间、创建人等信息。
数据库设计表如下所示。
记录明细大概如下所示。
为了管理好这些文件信息,我们在界面提供一些条件供查询,如下是管理界面。
为了快速的进行检索,我们提供了两个树形列表进行查询,可以按照文件类型,以及按照类别名称查询,类别是我们在上传的时候指定的一个附件的类别名称。
按文件类型分类如下所示
按类别名称分类如下所示。
而树形列表的信息展示,我们使用了自定义的树列表控件,非常方便,并极大减少了界面代码,界面代码如下所示。
<el-tabs value="treeType" type="border-card"> <el-tab-pane name="treeType" label="按文件类型"> <myTree :data="treeType" icon-class="el-icon-price-tag" @nodeClick="nodeTypeClick" /> </el-tab-pane> <el-tab-pane name="treeCategory" label="按类别名称"> <myTree :data="treeCategory" icon-class="el-icon-price-tag" @nodeClick="nodeCategoryClick" /> </el-tab-pane> </el-tabs>
而树形列表的类别名称,我们是从数据库中动态获取的,因此需要特殊的API封装调用。
在ABP框架的后端,应用服务类FileUploadAppService中定义一个获取类别的列表接口
/// <summary> /// 获取所有类别(Distinct) /// </summary> /// <returns></returns> public virtual async Task<List<string>> GetAllCategory() { var query = Repository.GetAll().Where(s=> s.Category != null).OrderBy(s => s.Category); var list = query.Select(s => s.Category).Distinct().ToList(); return await Task.FromResult(list); }
在客户端的API调用类中同时增加一个API处理接口,如下所示。
而Element的前端调用后端的ABP接口,前面很多博客也介绍的很多了,如下是它们的处理过程图示。
前端根据ABP后端的接口进行前端JS端的类的封装处理,引入了ES6类的概念实现业务基类接口的统一封装,简化代码。
大多数模块我们涉及到常规增删改查等业务接口,那么这些类继承BaseApi,就会具有相关的接口了,如下所示继承关系。
其中JS类的BaseApi具有常规的增删改查接口,如下所示。
在整合ABP后端接口的时候,我们为了方便,一般使用ES6的方式定义一个客户端的Api调用类,基础接口封装在BaseApi类里面,扩展自定义接口放在子类定义,因此前端API封装类fileupload.js的类关系如下所示。
我们再次回到管理界面,在列表中展示附件信息外,如果是图片提供预览,如果是文件则提供下载链接,方便处理。
或者
预览查看图片文件的时候,我们也需要在明细中列出附件的一些信息,如下界面所示。
以上就是附件管理的设计表,以及管理界面,其中前端主要使用了Vue + Element进行开发,后端还是用ABP的框架提供相关的API接口。
2、附件上传的处理
在之前随笔《循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理》中已经比较详细的介绍了对附件的上传处理,我们ABP后端提供了一些API接口给前端界面控件进行调用即可上传对应的附件
在附件上传处理的时候,我们就可以通过这样获得请求的文件对象了,如下代码所示。
我们上传到后端ABP应用服务器的文件,一般情况是不能访问目录的,如果需要特别放行,那么需要在ABP服务的Host应用里面,设置静态文件,允许前端访问我们的文件路径。
一般在Host项目的启动入口设置即可。
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { ............ app.UseStaticFiles(); //指定特定的目录作为静态文件目录,如UploadFiles //是否可以访问静态文件 app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "UploadFiles")), RequestPath = "/UploadFiles" }); .........
这样上传的文件,在对应目录里面,就可以通过URL地址访问了。
另外,前面我们看到的数据记录里面,没有绝对的URL地址,一般是为了适应性方便,不需要绝对的地址。
但是前端为了方便,服务器后端返回的接口中,我们一般增加一个绝对的地址信息供查看或者下载文件,那么我们可以在后端对应附件记录的转义函数里面增加一个对相对地址转换为绝对访问的URL地址的转换即可。
/// <summary> /// 对记录进行转义 /// </summary> /// <param name="item">dto数据对象</param> /// <returns></returns> protected override void ConvertDto(FileUploadDto item) { //转义相对地址为绝对地址 item.FileUrl = GetFileUrl(item.BasePath, item.SavePath); }
/// <summary> /// 根据记录的basePath和savePath,以及HttpContext上下文确定绝对路径 /// </summary> /// <param name="basePath">附件的基础路径</param> /// <param name="savePath">附件的保存路径</param> /// <returns></returns> private string GetFileUrl(string basePath, string savePath) { var httpContext = _httpContext.HttpContext; string serverRealPath = basePath.UriCombine(savePath); if (!Path.IsPathRooted(basePath) && !basePath.StartsWith("http://") && !basePath.StartsWith("https://")) { //如果是相对目录,加上当前程序的目录才能定位文件地址 var url = string.Format("{0}://{1}", httpContext.Request.Scheme, httpContext.Request.Host.Value); serverRealPath = url.UriCombine(serverRealPath).Replace('\', '/'); } return serverRealPath; }
而前端界面中,一般的图片和附件上传界面如下所示。
编辑界面下,附件上传界面,可以加载已有的记录展示,如下所示。
用图片列表控件的方式展示图片信息,如下所示。
如果我们用Element的上传组件,大概的界面代码如下所示,主要设置好上传的API地址,以及给它提供好对应的授权头部信息即可。
前端界面的代码如下所示。
<el-form-item label="封面图片"> <el-upload ref="upload" action="/abp/services/app/FileUpload/PostUpload" list-type="picture-card" :on-preview="handlePictureCardPreview" :on-remove="handleRemove" :on-success="onSuccess" :on-error="onError" accept="image/jpeg,image/gif,image/png,image/bmp" :headers="myHeaders" :file-list="editForm.fileList" > <i class="el-icon-plus" /> </el-upload> <el-dialog :visible.sync="dialogVisible"> <img width="100%" :src="dialogImageUrl" alt=""> </el-dialog> </el-form-item>
其中授权头部信息,是ABP约定的授权请求头部信息,如下属性设置
myHeaders: { Authorization: 'Bearer ' + getToken() }, // 用于上传文件的身份认证
这样就可以整合文件上传的管理操作了,而前端就只需要针对附件信息,做统一的管理即可。
如下是统一的附件管理界面入口。
为了方便读者理解,我列出一下前面几篇随笔的连接,供参考:
循序渐进VUE+Element 前端应用开发(1)--- 开发环境的准备工作
循序渐进VUE+Element 前端应用开发(2)--- Vuex中的API、Store和View的使用
循序渐进VUE+Element 前端应用开发(3)--- 动态菜单和路由的关联处理
循序渐进VUE+Element 前端应用开发(4)--- 获取后端数据及产品信息页面的处理
循序渐进VUE+Element 前端应用开发(5)--- 表格列表页面的查询,列表展示和字段转义处理
循序渐进VUE+Element 前端应用开发(6)--- 常规Element 界面组件的使用
循序渐进VUE+Element 前端应用开发(7)--- 介绍一些常规的JS处理函数
循序渐进VUE+Element 前端应用开发(8)--- 树列表组件的使用
循序渐进VUE+Element 前端应用开发(9)--- 界面语言国际化的处理
循序渐进VUE+Element 前端应用开发(10)--- 基于vue-echarts处理各种图表展示
循序渐进VUE+Element 前端应用开发(11)--- 图标的维护和使用
循序渐进VUE+Element 前端应用开发(12)--- 整合ABP框架的前端登录处理
循序渐进VUE+Element 前端应用开发(13)--- 前端API接口的封装处理
循序渐进VUE+Element 前端应用开发(14)--- 根据ABP后端接口实现前端界面展示
循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理
循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理
循序渐进VUE+Element 前端应用开发(17)--- 菜单管理
循序渐进VUE+Element 前端应用开发(18)--- 功能点管理及权限控制
循序渐进VUE+Element 前端应用开发(19)--- 后端查询接口和Vue前端的整合
循序渐进VUE+Element 前端应用开发(20)--- 使用组件封装简化界面代码
循序渐进VUE+Element 前端应用开发(21)--- 省市区县联动处理的组件使用
循序渐进VUE+Element 前端应用开发(22)--- 简化main.js处理代码,抽取过滤器、全局界面函数、组件注册等处理逻辑到不同的文件中
循序渐进VUE+Element 前端应用开发(23)--- 基于ABP实现前后端的附件上传,图片或者附件展示管理
循序渐进VUE+Element 前端应用开发(24)--- 修改密码的前端界面和ABP后端设置处理
循序渐进VUE+Element 前端应用开发(25)--- 各种界面组件的使用(1)
循序渐进VUE+Element 前端应用开发(26)--- 各种界面组件的使用(2)
ABP框架中一对多,多对多关系的处理以及功能界面的处理(1)
ABP框架中一对多,多对多关系的处理以及功能界面的处理(2)