Tips:本篇已加入系列文章阅读目录,可点击查看更多相关文章。

前言

在之前的章节中介绍过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来安装引用。两种方式各有优缺点,具体请参考自定义现有模块,关于私有NuGet搭建可以参考:十分钟搭建自己的私有NuGet服务器-BaGet

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

通过上面的方式引用模块,使用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系统可以了解一下。

初识ABP vNext(9):ABP模块化开发-文件管理的更多相关文章

  1. 基于 abp vNext 和 .NET Core 开发博客项目 - 定时任务最佳实战(三)

    上一篇(https://www.cnblogs.com/meowv/p/12974439.html)完成了全网各大平台的热点新闻数据的抓取,本篇继续围绕抓取完成后的操作做一个提醒.当每次抓取完数据后, ...

  2. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  3. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  4. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(三)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  5. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(四)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  6. 基于 abp vNext 和 .NET Core 开发博客项目 - 博客接口实战篇(五)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  7. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  8. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  9. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(三)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  10. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(四)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

随机推荐

  1. C#LeetCode刷题-随机数

    随机数篇 # 题名 刷题 通过率 难度 470 用 Rand7() 实现 Rand10()   34.4% 中等 478 在圆内随机生成点   22.8% 中等 497 非重叠矩形中的随机点   22 ...

  2. 三、HelloWorld

    1.创建Hello.java 文件, 2.输入内容 public class Hello{ //公共类 Hello public static void main(String[] args){ // ...

  3. Qt开发技术:QCharts(三)QCharts样条曲线图介绍、Demo以及代码详解

    若该文为原创文章,未经允许不得转载原博主博客地址:https://blog.csdn.net/qq21497936原博主博客导航:https://blog.csdn.net/qq21497936/ar ...

  4. python 去除Excel中的重复行数据

    导入pandas import pandas as pd 1.读取excel中的数据: frame = pd.DataFrame(pd.read_csv('excel的绝对路径.csv'', 'She ...

  5. 使用正则表达式限制<kbd>QLineEdit</kbd>不能输入大于某个整数

    使用正则表达式限制QLineEdit不能输入大于某个整数,即:使用正则表达式,匹配不大于某个整数 举例:匹配不大于4567 * 原理分析: 匹配不大于4567这个数,所以第一个想到的是只要小于4567 ...

  6. centos7下的redis集群模式

    1.先安装好单机版的redis 2.Reids安装包里有个集群工具,要复制到/usr/local/bin里去 cd /home/redis/redis-4.0./src ls - cp redis-t ...

  7. go genetlink demo

    原文链接:https://github.com/mdlayher/genetlink [root@wangjq test]# cat genetlink.go package main import ...

  8. Linux环境下如何生成core文件

    Linux环境下进程发生异常而挂掉,通常很难查找原因,但是一般Linux内核给我们提供的核心文件,记录了进程在崩溃时候的信息.但是生成core文件需要设置开关,具体步骤如下: 1.查看生成core文件 ...

  9. c++右值引用和转移构造函数

    int &&i = ; //i绑定到了右值1 int b = ; cout << i << endl; //输出1 i = b; cout << i ...

  10. RabbitMQ高级之如何保证消息可靠性?

    人生终将是场单人旅途,孤独之前是迷茫,孤独过后是成长. 楔子 本篇是消息队列RabbitMQ的第四弹. RabbitMQ我已经写了三篇了,基础的收发消息和基础的概念我都已经写了,学任何东西都是这样,先 ...