初识ABP vNext(9):ABP模块化开发-文件管理

点击上方蓝字"小黑在哪里"关注我吧

  • 创建模块

  • 模块开发

    • 应用服务

    • 运行模块

    • 单元测试

  • 模块使用

前言

在之前的章节中介绍过ABP扩展实体,当时在用户表扩展了用户头像字段,用户头像就涉及到文件上传和文件存储。文件上传是很多系统都会涉及到的一个基础功能,在ABP的模块化思路下,文件管理可以做成一个通用的模块,便于以后在多个项目中复用。单纯实现一个文件上传的功能并不复杂,本文就借着这个简单的功能来介绍一下ABP模块化开发的最基本步骤。

开始

创建模块

首先使用ABP CLI创建一个模块:abp new Xhznl.FileManagement -t module --no-ui

创建完成后会得到如下文件:

在主项目中添加对应模块的引用,Application=>Application,Domain=>Domain,HttpApi=>HttpApi 等等。例如:

需要添加引用的项目:Application、Application.Contracts、Domain、Domain.Shared、EntityFrameworkCore、HttpApi、HttpApi.Client

手动添加这些引用比较麻烦,你可以搭建自己的私有NuGet服务器,把模块的包发布到私有NuGet上,然后通过NuGet来安装引用。两种方式各有优缺点,具体请参考自定义现有模块[1],关于私有NuGet搭建可以参考:十分钟搭建自己的私有NuGet服务器-BaGet[2]

然后给这些项目的模块类添加对应的依赖,例如:

通过上面的方式引用模块,使用visual studio是无法编译通过的:

需要在解决方案目录下,手动执行dotnet restore命令即可:

模块开发

接下来关于文件管理功能的开发,都在模块Xhznl.FileManagement中进行,它是一个独立的解决方案。初学ABP,下面就以尽量简单的方式来实现这个模块。

应用服务

模块开发通常从Domain层实体建立开始,但是这里先跳过。先在FileManagement.Application.Contracts项目添加应用服务接口和Dto。

modules\file-management\src\Xhznl.FileManagement.Application.Contracts\Files\IFileAppService.cs:

public interface IFileAppService : IApplicationService
{Task<byte[]> GetAsync(string name);Task<string> CreateAsync(FileUploadInputDto input);
}

modules\file-management\src\Xhznl.FileManagement.Application.Contracts\Files\FileUploadInputDto.cs:

public class FileUploadInputDto
{[Required]public byte[] Bytes { get; set; }[Required]public string Name { get; set; }
}

然后是FileManagement.Application项目,实现应用服务,先定义一个配置类。

modules\file-management\src\Xhznl.FileManagement.Application\Files\FileOptions.cs:

public class FileOptions
{/// <summary>/// 文件上传目录/// </summary>public string FileUploadLocalFolder { get; set; }/// <summary>/// 允许的文件最大大小/// </summary>public long MaxFileSize { get; set; } = 1048576;//1MB/// <summary>/// 允许的文件类型/// </summary>public string[] AllowedUploadFormats { get; set; } = { ".jpg", ".jpeg", ".png", "gif", ".txt" };
}

modules\file-management\src\Xhznl.FileManagement.Application\Files\FileAppService.cs:

public class FileAppService : FileManagementAppService, IFileAppService
{private readonly FileOptions _fileOptions;public FileAppService(IOptions<FileOptions> fileOptions){_fileOptions = fileOptions.Value;}public Task<byte[]> GetAsync(string name){Check.NotNullOrWhiteSpace(name, nameof(name));var filePath = Path.Combine(_fileOptions.FileUploadLocalFolder, name);if (File.Exists(filePath)){return Task.FromResult(File.ReadAllBytes(filePath));}return Task.FromResult(new byte[0]);}[Authorize]public Task<string> CreateAsync(FileUploadInputDto input){if (input.Bytes.IsNullOrEmpty()){throw new AbpValidationException("Bytes can not be null or empty!",new List<ValidationResult>{new ValidationResult("Bytes can not be null or empty!", new[] {"Bytes"})});}if (input.Bytes.Length > _fileOptions.MaxFileSize){throw new UserFriendlyException($"File exceeds the maximum upload size ({_fileOptions.MaxFileSize / 1024 / 1024} MB)!");}if (!_fileOptions.AllowedUploadFormats.Contains(Path.GetExtension(input.Name))){throw new UserFriendlyException("Not a valid file format!");}var fileName = Guid.NewGuid().ToString("N") + Path.GetExtension(input.Name);var filePath = Path.Combine(_fileOptions.FileUploadLocalFolder, fileName);if (!Directory.Exists(_fileOptions.FileUploadLocalFolder)){Directory.CreateDirectory(_fileOptions.FileUploadLocalFolder);}File.WriteAllBytes(filePath, input.Bytes);return Task.FromResult("/api/file-management/files/" + fileName);}
}

服务实现很简单,就是基于本地文件系统的读写操作。

下面是FileManagement.HttpApi项目,添加控制器,暴露服务API接口。

modules\file-management\src\Xhznl.FileManagement.HttpApi\Files\FileController.cs:

[RemoteService]
[Route("api/file-management/files")]
public class FileController : FileManagementController
{private readonly IFileAppService _fileAppService;public FileController(IFileAppService fileAppService){_fileAppService = fileAppService;}[HttpGet][Route("{name}")]public async Task<FileResult> GetAsync(string name){var bytes = await _fileAppService.GetAsync(name);return File(bytes, MimeTypes.GetByExtension(Path.GetExtension(name)));}[HttpPost][Route("upload")][Authorize]public async Task<JsonResult> CreateAsync(IFormFile file){if (file == null){throw new UserFriendlyException("No file found!");}var bytes = await file.GetAllBytesAsync();var result = await _fileAppService.CreateAsync(new FileUploadInputDto(){Bytes = bytes,Name = file.FileName});return Json(result);}}

运行模块

ABP的模板是可以独立运行的,在FileManagement.HttpApi.Host项目的模块类FileManagementHttpApiHostModule配置FileOptions:

修改FileManagement.HttpApi.Host和FileManagement.IdentityServer项目的数据库连接配置,然后启动这2个项目,不出意外的话可以看到如下界面。

FileManagement.HttpApi.Host:

FileManagement.IdentityServer:

现在你可以使用postman来测试一下File的2个API,当然也可以编写单元测试。

单元测试

更好的方法是编写单元测试,关于如何做好单元测试可以参考ABP源码,下面只做一个简单示例:

模块使用

模块测试通过后,回到主项目。模块引用,模块依赖前面都已经做好了,现在只需配置一下FileOptions,就可以使用了。

目前FileManagement.Domain、FileManagement.Domain.Shared、FileManagement.EntityFrameworkCore这几个项目暂时没用到,项目结构也不是固定的,可以根据自己实际情况来调整。

最后

本文的模块示例比较简单,只是完成了一个文件上传和显示的基本功能,关于实体,数据库,领域服务,仓储之类的都暂时没用到。但是相信可以通过这个简单的例子,感受到ABP插件式的开发体验,这是一个好的开始,更多详细内容后面再做介绍。本文参考了ABP blogging模块的文件管理,关于文件存储,ABP中也有一个BLOB系统可以了解一下。

参考资料

[1]

自定义现有模块: https://docs.abp.io/zh-Hans/abp/latest/Customizing-Application-Modules-Guide

[2]

十分钟搭建自己的私有NuGet服务器-BaGet: https://www.cnblogs.com/xhznl/p/13426918.html

如果本文对您有用,

不妨点个“”或者转发朋友圈支持一下

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/307829.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「offer来了」浏览器原理被问懵?5大知识板块巩固你的http知识体系(3.6w字)

「面试专栏」前端面试之浏览器原理篇&#x1f3d4;️序言&#x1f304;一、http和https协议&#xff08;一&#xff09;http和https之间的关系&#x1f9ed;1、http和https是什么&#xff1f;2、http和https的区别&#xff08;二&#xff09;http协议&#x1f9ed;1、http1.0、…

使用Azure DevOps Pipeline实现.Net Core程序的CD

上一次我们讲了使用Azure DevOps Pipeline实现.Net Core程序的CI。这次我们来演示下如何使用Azure DevOps实现.Net Core程序的CD。实现本次目标我们除了Azure DevOps外还需要&#xff1a;一台安装了Docker的主机一个 Docker Hub 账号上一次我们的CI实现了&#xff1a;发布>编…

TCP四次挥手(详解)

一:TCP四次挥手 1:图示 二:TCP四次挥手的过程 所谓的四次挥手即TCP连接的释放(解除)。连接的释放必须是一方主动释放&#xff0c;另一方被动释放。挥手之前主动释放连接的客户端结束ESTABLISHED阶段。随后开始“四次挥手”&#xff1a; a:首先客户端想要释放连接&#xff0c…

「软件项目管理」项目初始——项目确立与生存期模型

「软件项目管理」项目初始阶段——项目确立与生存期模型&#x1f6f0;️序言Preface&#x1f680;一、项目评估1、评估内容2、净利润与投资回报率3、举例阐述&#x1fa90;二、项目立项1、立项流程2、Make or Buy决策3、Make or Buy决策实例&#x1f6f8;三、项目招投标1、项目…

双城生活,一种相对无奈且幸福的选择

这是头哥侃码的第215篇原创我小时候经常被人问到一个问题&#xff1a;“你喜欢夏天还是冬天&#xff1f;”“夏天啊&#xff01;因为夏天可以有两个月的暑假&#xff0c;而且还可以玩水&#xff0c;还有清凉的盐水棒冰、短裤和凉拖&#xff0c;还可以在空调间里打游戏&#xff…

leetcode450. 删除二叉搜索树中的节点(详解)

一:题目 二:上码 1.确定递归函数和参数 TreeNode* deleteNode(TreeNode* root, int key) 这里的返回参数 我们也用一个指针接住&#xff0c;反正我们最终是返回的整棵树&#xff08;如果没找到那就是空&#xff09; 2.确定递归函数的终止条件 if(root NULL) return root; 3.确…

「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识

「面试专栏」前端面试之操作系统篇&#x1f3b9;序言&#x1f3b8;一、思维导图&#x1f3ba;二、常见面试题1、进程和线程以及它们的区别2、进程间通信的几种方式&#xff08;1&#xff09;管道(pipe)及命名管道(named pipe)&#xff08;2&#xff09;信号(signal)&#xff08…

推荐一个IT老鸟肝了2月有余的免费开源WPF企业级开发框架

JHRS一个新学WPF的IT老鸟&#xff0c;肝了2个月做了这么一个WPF企业级开发框架&#xff0c;站长clone学习&#xff0c;觉得甚是不错。这是一个使用了Prism搭建的插件式框架&#xff0c;封装了DataGrid的使用&#xff0c;使整个框架子模块简单易学、易扩展&#xff0c;特别是作者…

「offer来了」从基础到进阶原理,从vue2到vue3,48个知识点保姆级带你巩固vuejs知识体系

「面试专栏」前端面试之vuejs篇&#x1f5bc;️序言&#x1f399;️一、vue2.x基础知识预备&#x1f4fb;二、vue2.x基础知识常见面试题1、请说出vue.cli项目中src目录每个文件夹和文件的用法&#xff1f;2、vue.cli中怎样使用自定义的组件&#xff1f;有遇到过哪些问题&#x…

.NET Core 下使用 Apollo 配置中心

“Apollo&#xff08;阿波罗&#xff09;是携程框架部门研发的分布式配置中心&#xff0c;能够集中化管理应用不同环境、不同集群的配置&#xff0c;配置修改后能够实时推送到应用端&#xff0c;并且具备规范的权限、流程治理等特性&#xff0c;适用于微服务配置管理场景。服务…

「offer来了」从基础配置到高级配置,16大知识点带你巩固webpack知识体系

「面试专栏」前端面试之Webpack篇&#x1f9e9;序言&#x1f3a8;一、基础知识学习&#x1f3b2;二、常见面试题汇总&#x1f3af;三、构建和打包1、前端代码如何进行构建和打包&#xff1f;2、前端为何要进行打包和构建&#xff1f;3、webpack原理&#x1f3b0;四、模块相关1、…

进击吧!Blazor!第一期回顾

Blazor 是一个 Web UI 框架&#xff0c;可通过 WebAssembly 在任意浏览器中运行 .Net 。Blazor 旨在简化快速的单页面 .Net 浏览器应用的构建过程&#xff0c;它虽然使用了诸如 CSS 和 HTML 之类的 Web 技术&#xff0c;但它使用 C&#xff03;语言和 Razor 语法代替 JavaScrip…

《五分钟商学院》个人篇学习总结(下)

【商业知识】| 作者 / Edison Zhou这是EdisonTalk的第286篇原创内容商业篇聚焦的是我们与外部的关系&#xff0c;管理篇聚焦的是我们与内部的关系&#xff0c;而个人篇聚焦的则是我们与自己的关系。与自己斗&#xff0c;其乐无穷&#xff0c;本文是个人篇的下半部分学习总结。上…

「软件项目管理」软件项目范围计划——需求管理与任务分解

软件项目范围计划——需求管理与任务分解序言一、软件需求定义及层次1、定义2、层次二、软件需求管理过程1、管理过程2、需求获取3、需求分析4、需求规格编写5、需求验证6、需求变更&#xff08;1&#xff09;需求变更管理的主要工作&#xff08;2&#xff09;需求变更控制流程…

leetcode106. 从中序与后序遍历序列构造二叉树

一:题目 二:思路 例子:中序[9,3,15,20,7];后序[9,15,7,20,3] 1.我们先选取后续的最后的结点3&#xff08;其是根节点&#xff09; 2.我们在中序序列中用上一步求出的根节点3并记录其在中序数组中的位置rootin,然后我们就可以 求出 左子树的结点个数&#xff08;rootin - leftin…

「offer来了」快来关注这些性能优化问题

「面试专栏」前端面试之性能优化篇&#x1f4ac;序言&#x1f4af;思维导图抢先看&#x1f441;️‍&#x1f5e8;️一、html、css、js、jq优化1、针对HTML&#xff0c;如何优化性能&#xff1f;2、针对CSS&#xff0c;如何优化性能&#xff1f;3、哪些方法能提升移动端CSS3动画…

不宜过分炒作第三代半导体材料弯道超车

目前&#xff0c;本土晶圆大厂与台积电在技术水平上的差距是非常明显的。就市场份额来说&#xff0c;台积电的市场份额超过50%&#xff0c;而本土晶圆大厂的市场份额大约是台积电的十分之一。在技术水平上&#xff0c;本土晶圆大厂也和台积电这样的大厂有一定差距。目前台积电7…

「offer来了」浅谈前端面试中开发环境常考知识点

「面试专栏」前端面试之开发环境篇&#x1f331;序言&#x1f332;一、git1、为什么要使用git?2、常用的git命令3、git提交规范&#x1f333; 二、Chrome调试工具&#x1f334;三、抓包1、抓包工具2、抓包过程&#x1f33e;四、linux 常用命令1、为什么要用linux&#xff1f;2…

数据结构与算法专题——第十题 输入法跳不过的坎-伸展树

我们知道AVL树为了保持严格的平衡&#xff0c;所以在数据插入上会呈现过多的旋转&#xff0c;影响了插入和删除的性能&#xff0c;此时AVL的一个变种伸展树&#xff08;Splay&#xff09;就应运而生了&#xff0c;我们知道万事万物都遵循一个“八二原则“&#xff0c;也就是说8…

「软件项目管理」一文详解软件项目成本计划

软件项目成本计划序言一、成本估算的定义二、估算的基本概念1、关于估算2、软件项目规模3、软件规模单位4、软件项目成本5、成本单位6、软件规模和软件成本的关系7、成本估算结果三、成本估算过程1、估算输入2、估算处理3、估算输出四、成本估算方法1、代码行估算法&#xff08…