初识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
如果本文對您有用,
不妨點個“在看”或者轉發朋友圈支持一下
總結
以上是生活随笔為你收集整理的初识ABP vNext(9):ABP模块化开发-文件管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进击吧! Blazor !第二期 页面制
- 下一篇: 使用Azure DevOps Pipel