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系統可以了解一下,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/35127.html
標籤:.NET Core
