miniIO系列文章03---abpvext中集成
在Abp商业版本中已经提供了文件管理模块的,免费版本是没有的,本文将介绍如何使用Minio打造一个自己的文件管理模块。
在项目开始之前,需要先安装一个Minio服务,可以在本地pc或云主机中安装,具体可见Minio中文文档,这里只介绍docker-compose方式安装
windows中安装docker desktop并切换到linux容器
linux中安装docker和docker-compose
任意选择一个目录作为minio安装目录,创建docker-compose.yml文件,输入如下内容:
version: '3.7' services: minio: image: minio/minio hostname: "minio" ports: - 50010:9000 # api 端口 - 50011:9001 # 控制台端口 environment: MINIO_ROOT_USER: admin #管理后台用户名 MINIO_ROOT_PASSWORD: 123qwe!@# #管理后台密码,最小8个字符 volumes: - /docker/minio/data:/data #映射当前目录下的data目录至容器内/data目录 - /docker/minio/config:/root/.minio/ #映射配置目录 command: server --console-address ':9001' /data #指定容器中的目录 /data privileged: true restart: always
执行命令创建并运行容器
docker-compose up -d
成功后浏览器打开http://localhost:50011/,出现MinIO登录页面,输入docker-compose.yml中定义的用户名和密码登录
登录后选择Buckets目录,添加一个files库作为文件管理的库,如图
选择Users菜单,添加一个test用户,并赋予读写权限
接着开始编写后台代码,步骤如下:
1、生成框架代码
可以使用Abp官方的代码模板或者前一章节中自定义的项目模板生成框架代码
自定义代码框架模板,见上一章:
abp new MyCompanyName.TestModuleProject -u angular --mobile none -d ef -csf -cs "server=192.168.100.175;port=3306;database=abp_test3;uid=test;pwd=Test123$;SslMode=none" -ts "F:\BlogSamples\templates\app" -v 5.0.0-rc.1
或者使用官方默认的代码框架模板(上面命令去掉-ts参数即可)
abp new MyCompanyName.TestModuleProject -u angular --mobile none -d ef -csf -cs "server=192.168.100.175;port=3306;database=abp_test3;uid=test;pwd=Test123$;SslMode=none"
如果使用官方模板,需要在Api.Host项目的模块文件中添加文件上传下载的配置,具体操作参考上一章内容
修改angular目录名为filemanagement.angular,这么做是避免在添加模块时生成angular的代码(自动生成的前端有点乱),接着进入aspnet-core目录执行以下命令在后台添加文件模块
abp add-module MyCompany.FileManagement --new --add-to-solution-file
添加完模块后启动模块的项目也自动添加项目引用和模块依赖,打开MyCompanyName.TestModuleProject.sln解决方案查看src目录下每个项目的Module文件,比如Application项目的TestProjectApplicationModule.cs文件
2、在文件模块中添加Minio支持
首先在MyCompany.TestProject.HttpApi.Host项目的appsettings.json配置文件中添加minio的连接参数配置,如下:
{ ... ... "Minio": { "EndPoint": "localhost:50010", "User": "test", "Password": "test123$%^", "BucketName": "files" } }
双击MyCompany.FileManagement.Domain项目,添加如下引用:
<ItemGroup> <PackageReference Include="Volo.Abp.BlobStoring" Version="5.0.0-rc.1" /> <PackageReference Include="Volo.Abp.BlobStoring.Minio" Version="5.0.0-rc.1" /> ... ... </ItemGroup>
打开FileManagementDomainModule.cs文件,修改为以下内容:
using Microsoft.Extensions.DependencyInjection; using Volo.Abp.BlobStoring; using Volo.Abp.BlobStoring.Minio; using Volo.Abp.Domain; using Volo.Abp.Modularity; namespace MyCompany.FileManagement { [DependsOn( typeof(AbpDddDomainModule), typeof(FileManagementDomainSharedModule), typeof(AbpBlobStoringMinioModule) )] public class FileManagementDomainModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) { // 获取appsettings配置 var configuration = context.Services.GetConfiguration(); // 配置使用Minio作为Blob存储的容器 Configure<AbpBlobStoringOptions>(options => { options.Containers.ConfigureDefault(container => { container.UseMinio(minio => { minio.EndPoint = configuration["Minio:EndPoint"]; // your minio endPoint minio.AccessKey = configuration["Minio:User"]; // your minio accessKey minio.SecretKey = configuration["Minio:Password"]; // your minio secretKey minio.BucketName = configuration["Minio:FilesBucket"]; // your minio bucketName }); }); }); } } }
3、添加数据库存储
Mino作为文件存储的容器,我们还需要一个表来存储文件的基本信息和目录关系,步骤如下:
(1)在FileManagement.Domain项目中添加Entities目录,在其中添加BlobFile.cs文件,内容如下:
using System; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; namespace MyCompany.FileManagement.Entities { public class BlobFile : AuditedAggregateRoot<Guid>, IMultiTenant { public long FileSize { get; set; } public string MimeType { get; set; } public Guid? TenantId { get; set; } public string Path { get; set; } public bool IsDirectory { get; set; } public string Name { get; set; } // 用户id,不为空的为个人文件,否则为公共文件 public Guid? OwnerId { get; set; } protected BlobFile() { } public BlobFile( Guid id, string name, long fileSize, string mimeType, string path, Guid? tenantId = null, Guid? ownerId = null, bool isDir = false ) : base(id) { Name = name; FileSize = fileSize; MimeType = mimeType; TenantId = tenantId; Path = path; OwnerId = ownerId; IsDirectory = isDir; } } }
继续添加文件管理数据仓库接口IBlobFileRepository.cs,代码如下:
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories; namespace MyCompany.FileManagement.Entities { /// <summary> /// 文件管理数据仓库接口 /// </summary> public interface IBlobFileRepository : IBasicRepository<BlobFile, Guid> { // 根据条件获取分页记录 Task<List<BlobFile>> GetListAsync(string sorting = null, int maxResultCount = int.MaxValue, int skipCount = 0, string path = null, Guid? userId = null, string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default); // 根据条件获取记录总数 Task<long> GetCountAsync(string path = null, Guid? userId = null, string filter = null, CancellationToken cancellationToken = default); // 获取子目录中文件记录 Task<List<BlobFile>> GetChildByPathAsync(string path, Guid? userId = null, CancellationToken cancellationToken = default); // 获取已用存储空间大小 Task<long> GetStorageSizeAsync(Guid? userId, CancellationToken cancellationToken = default); } }
最后添加一个领域服务类,领域服务提供个人文件和公共文件的存储和访问方法,FileManagementManager.cs
using MyCompany.FileManagement.Entities; using Shktiot.FileManagement; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.BlobStoring; using Volo.Abp.Domain.Services; namespace MyCompany.FileManagement { public class FileManagementManager: DomainService { // 注入类型化Blob容器接口 private readonly IBlobContainer<FilesContainer> _blobContainer; // 注入文件管理数据仓库接口 private readonly IBlobFileRepository _fileRepository; public FileManagementManager(IBlobContainer<FilesContainer> blobContainer, IBlobFileRepository fileRepository) { _blobContainer = blobContainer; _fileRepository = fileRepository; } /// <summary> /// 上传文件,userId为空时是公共文件 /// </summary> /// <param name="path"></param> /// <param name="fileName"></param> /// <param name="bytes"></param> /// <param name="contentType"></param> /// <param name="userId"></param> /// <returns></returns> /// <exception cref="UserFriendlyException"></exception> public async Task<string> CreateAsync(string path, string fileName, byte[] bytes, string contentType, Guid? userId = null) { // 获取当前已用用户空间大小 var userUsedSize = await _fileRepository.GetStorageSizeAsync(userId); var userTotalSize = bytes.Length + userUsedSize; if (userTotalSize > 100000000) { throw new UserFriendlyException("剩余空间不足!"); } var filePath = FormatPathName(path); var newFile = new BlobFile(GuidGenerator.Create(), fileName, bytes.Length, contentType, filePath, CurrentTenant.Id, userId); var created = await _fileRepository.InsertAsync(newFile); await _blobContainer.SaveAsync(newFile.Id.ToString(), bytes).ConfigureAwait(false); return filePath; } public async Task<string> CreateDirectoryAsync(string path, string dirName, Guid? userId = null) { var dirPath = FormatPathName(path); var newDir = new BlobFile(GuidGenerator.Create(), dirName, 0, null, dirPath, CurrentTenant.Id, userId, true); var created = await _fileRepository.InsertAsync(newDir); return created.Path; } public async Task DeleteDirAsync(Guid id, Guid? userId = null) { var dir = await _fileRepository.GetAsync(id); if (!dir.IsDirectory) { throw new UserFriendlyException("目录不存在!"); } var dirPath = ConbinePathName(dir.Path, dir.Name); var chidren = await _fileRepository.GetChildByPathAsync(dirPath, userId); await _fileRepository.DeleteManyAsync(chidren); foreach (var item in chidren) { await _blobContainer.DeleteAsync(item.Id.ToString()); } await _fileRepository.DeleteAsync(id); } public async Task DeleteFileAsync(Guid id) { var file = await _fileRepository.GetAsync(id); if (file.IsDirectory) { throw new UserFriendlyException("文件不存在!"); } await _blobContainer.DeleteAsync(id.ToString()); await _fileRepository.DeleteAsync(id); } public async Task<byte[]> GetBytesAsync(Guid id) { var file = await _blobContainer.GetAllBytesAsync(id.ToString()); return file; } public async Task<BlobFile> GetFileInfoAsync(Guid id) { var fileInfo = await _fileRepository.GetAsync(id); return fileInfo; } public Task<long> GetCountAsync(string path, string filter, Guid? userId = null) { var dir = FormatPathName(path); return _fileRepository.GetCountAsync(dir, userId, filter); } public Task<List<BlobFile>> GetListAsync(string sorting, int maxCount, int skipCount, string path, string filter, Guid? userId = null) { var dir = FormatPathName(path); return _fileRepository.GetListAsync(sorting, maxCount, skipCount, dir, userId, filter); } public async Task RenameAsync(Guid id, string newName) { var file = await _fileRepository.GetAsync(id); file.Name = newName; await _fileRepository.UpdateAsync(file); } protected string FormatPathName(string path) { path = $"/{path}/".Trim("./".ToArray()); if (path.Length == 0) return "/"; return $"/{path}/"; } private string ConbinePathName(string path, string name) { return $"{FormatPathName(path).TrimEnd('/')}/{name}"; } } } (2)添加EntityFrameworkCore配置 首先在IFileManagementDbContext.cs中添加BlobFile属性 public interface IFileManagementDbContext : IEfCoreDbContext { DbSet<BlobFile> BlobFiles { get; } } 在FileManagementDbContext.cs中添加BlobFile属性实现 public class FileManagementDbContext : AbpDbContext<FileManagementDbContext>, IFileManagementDbContext { public DbSet<BlobFile> BlobFiles { get; set; } ... } 在FileManagementDbContextModelCreatingExtensions.cs的ConfigureFileManagement方法中添加BlobFile实体的数据库配置 using Microsoft.EntityFrameworkCore; using MyCompany.FileManagement.Entities; using Volo.Abp; using Volo.Abp.EntityFrameworkCore.Modeling; namespace MyCompany.FileManagement.EntityFrameworkCore { public static class FileManagementDbContextModelCreatingExtensions { public static void ConfigureFileManagement( this ModelBuilder builder) { Check.NotNull(builder, nameof(builder)); builder.Entity<BlobFile>(b => { // 表名 b.ToTable(FileManagementDbProperties.DbTablePrefix + "BlobFiles", FileManagementDbProperties.DbSchema); // 一些基础实体字段的自动配置,比如ExreaProperties扩展字段等 b.ConfigureByConvention(); // 配置字段属性,长度、类型、是否为空等 b.Property(x => x.Name).HasMaxLength(256); b.Property(x => x.Path).HasMaxLength(256); b.Property(x => x.MimeType).HasMaxLength(128); // 定义索引 b.HasIndex(f => f.Name); b.HasIndex(f => f.Path); b.HasIndex(f => f.OwnerId); b.HasIndex(f => f.LastModificationTime); }); } } }
在EntityFrameworkCore目录中添加IBlobFileRepository数据仓库接口的实现类BlobFileRepository.cs
using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; using System.Linq.Dynamic.Core; using MyCompany.FileManagement.Entities; namespace MyCompany.FileManagement.EntityFrameworkCore { public class BlobFileRepository : EfCoreRepository<IFileManagementDbContext, BlobFile, Guid>, IBlobFileRepository { public BlobFileRepository( IDbContextProvider<IFileManagementDbContext> dbContextProvider) : base(dbContextProvider) { } public async Task<List<BlobFile>> GetChildByPathAsync(string path, Guid? userId = null, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .WhereIf(!path.IsNullOrWhiteSpace(), u => u.Path.StartsWith(path)) .Where(t => t.OwnerId == userId) .ToListAsync(GetCancellationToken(cancellationToken)); } public async Task<long> GetCountAsync(string path = null, Guid? userId = null, string filter = null, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .WhereIf(!path.IsNullOrWhiteSpace(), u => u.Path == path) .WhereIf(!filter.IsNullOrWhiteSpace(), u => u.Name.Contains(filter)) .Where(t => t.OwnerId == userId) .LongCountAsync(GetCancellationToken(cancellationToken)); } public async Task<List<BlobFile>> GetListAsync(string sorting = null, int maxResultCount = int.MaxValue, int skipCount = 0, string path = null, Guid? userId = null, string filter = null, bool includeDetails = false, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .WhereIf(!path.IsNullOrWhiteSpace(), u => u.Path == path) .WhereIf(!filter.IsNullOrWhiteSpace(), u => u.Name.Contains(filter)) .Where(t => t.OwnerId == userId) .OrderBy(sorting.IsNullOrWhiteSpace() ? nameof(BlobFile.Name) : sorting) .PageBy(skipCount, maxResultCount) .ToListAsync(GetCancellationToken(cancellationToken)); } public async Task<long> GetStorageSizeAsync(Guid? userId, CancellationToken cancellationToken = default) { return await (await GetDbSetAsync()) .WhereIf(userId.HasValue, u => u.OwnerId == userId) .SumAsync(t => t.FileSize, GetCancellationToken(cancellationToken)); } } }
(3) 生成数据库迁移
打开启动模块MyCompany.TestProject.EntityFrameworkCore项目中的TestProjectDbContext.cs文件,可以看到在执行add-module命令时已自动添加了文件模块的数据库配置方法:
protected override void OnModelCreating(ModelBuilder builder) { ... ... builder.ConfigureFileManagement(); }
右键该项目->在终端打开,或者在cmd命令行中进入项目所在目录,执行数据生成迁移命令:
dotnet ef migrations add -o Migrations InitDb
可以看到Migrations\XXXXXXX_InitDb.cs中有了创建FileManagementBlobFiles表的脚本
执行MyCompany.TestProject.DbMigrator项目,生成数据库
4、添加文件管理数据服务
数据服务提供前端代码访问的接口方法,需要添加以下内容:
(1)权限控制
首先在MyCompany.FileManagement.Application.Contracts项目的FileManagementPermissions.cs文件中添加两组权限定义的常量如下
public class FileManagementPermissions { ... ... // 个人文件 public static class MyFiles { public const string Default = GroupName + ".MyFiles"; public const string CreateDir = Default + ".CreateDir"; public const string UploadFile = Default + ".UploadFile"; public const string Rename = Default + ".Rename"; public const string Delete = Default + ".Delete"; public const string Download = Default + ".Download"; } // 公共文件 public static class PublicFiles { public const string Default = GroupName + ".PublicFiles"; public const string CreateDir = Default + ".CreateDir"; public const string UploadFile = Default + ".UploadFile"; public const string Rename = Default + ".Rename"; public const string Delete = Default + ".Delete"; public const string Download = Default + ".Download"; } } 然后在FileManagementPermissionDefinitionProvider中添加权限定义: public override void Define(IPermissionDefinitionContext context) { var fileManageGroup = context.AddGroup(FileManagementPermissions.GroupName, L("Menu:FileManagement")); var publicFiles = fileManageGroup.AddPermission(FileManagementPermissions.PublicFiles.Default, L("Menu:PublicFiles")); publicFiles.AddChild(FileManagementPermissions.PublicFiles.CreateDir, L("Files:CreateDir")); publicFiles.AddChild(FileManagementPermissions.PublicFiles.Rename, L("Files:ReName")); publicFiles.AddChild(FileManagementPermissions.PublicFiles.UploadFile, L("Files:UploadFile")); publicFiles.AddChild(FileManagementPermissions.PublicFiles.Delete, L("Files:Delete")); publicFiles.AddChild(FileManagementPermissions.PublicFiles.Download, L("Files:Download")); var myFiles = fileManageGroup.AddPermission(FileManagementPermissions.MyFiles.Default, L("Menu:MyFiles")); myFiles.AddChild(FileManagementPermissions.MyFiles.CreateDir, L("Files:CreateDir")); myFiles.AddChild(FileManagementPermissions.MyFiles.UploadFile, L("Files:UploadFile")); myFiles.AddChild(FileManagementPermissions.MyFiles.Rename, L("Files:ReName")); myFiles.AddChild(FileManagementPermissions.MyFiles.Delete, L("Files:Delete")); myFiles.AddChild(FileManagementPermissions.MyFiles.Download, L("Files:Download")); }
(2)数据服务接口
移除MyCompany.FileManagement.Application.Contracts项目中的Sample目录,添加FileBlob目录,在其中添加文件管理数据服务接口IBlobFileManageAppService.cs:
using System; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; namespace MyCompany.FileManagement.FileBlob { public interface IBlobFileManageAppService : IApplicationService { /// <summary> /// 获取文件 /// </summary> /// <param name="id"></param> /// <returns></returns> Task<RawFileDto> GetAsync(Guid id); /// <summary> /// 创建目录 /// </summary> /// <param name="input"></param> /// <returns></returns> Task<string> CreateDirectoryAsync(CreateDirInputDto input); /// <summary> /// 重命名 /// </summary> /// <param name="input"></param> /// <returns></returns> Task RenameAsync(RenameInputDto input); /// <summary> /// 上传创建文件 /// </summary> /// <param name="input"></param> /// <returns></returns> Task<FileUploadOutputDto> CreateAsync(FileUploadInputDto input); /// <summary> /// 获取文件列表 /// </summary> /// <param name="path"></param> /// <param name="input"></param> /// <returns></returns> Task<PagedResultDto<FileInfoDto>> GetListAsync(string path, GetDirectoryListInput input); /// <summary> /// 删除目录 /// </summary> /// <param name="id"></param> /// <returns></returns> Task DeleteDirAsync(Guid id); /// <summary> /// 删除文件 /// </summary> /// <param name="id"></param> /// <returns></returns> Task DeleteFileAsync(Guid id); } }
继续添加个人文件服务接口IMyFileManageAppService.cs
namespace MyCompany.FileManagement.FileBlob { public interface IMyFileManageAppService : IBlobFileManageAppService { } }
继续添加公共文件服务接口IPublicFileManageAppService.cs
namespace MyCompany.FileManagement.FileBlob { // 继承自文件服务,存储时用户Id为空时认为是公共文件 public interface IPublicFileManageAppService : IBlobFileManageAppService { } }
个人文件和公共文件的区分是存储时用户Id为空时认为是公共文件
(3)DTO数据传输对象
在MyCompany.FileManagementApplication.Contracts项目的FileBlob目录下添加以下Dto类:
CreateDirInputDto.cs
using System.ComponentModel.DataAnnotations;
namespace MyCompany.FileManagement.FileBlob
{
public class CreateDirInputDto
{
[Required]
public string Name { get; set; }
public string Path { get; set; }
}
}
FileInfoDto.cs
using System;
using Volo.Abp.Application.Dtos;
namespace MyCompany.FileManagement.FileBlob
{
public class FileInfoDto : EntityDto<Guid>
{
public DateTime CreationTime { get; set; }
public DateTime LastModificationTime { get; set; }
public long FileSize { get; set; }
public string MimeType { get; set; }
public string Path { get; set; }
public bool IsDirectory { get; set; }
public string Name { get; set; }
}
}
FileUploadInputDto.cs
using System.ComponentModel.DataAnnotations;
namespace MyCompany.FileManagement.FileBlob
{
public class FileUploadInputDto
{
[Required]
public byte[] Bytes { get; set; }
[Required]
public string Name { get; set; }
public string ContentType { get; set; }
public string Path { get; set; }
}
}
FileUploadOutputDto.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace MyCompany.FileManagement.FileBlob
{
public class FileUploadOutputDto
{
public string Name { get; set; }
public string WebUrl { get; set; }
}
}
GetDirectoryListInput.cs
using Volo.Abp.Application.Dtos;
namespace MyCompany.FileManagement.FileBlob
{
public class GetDirectoryListInput : PagedAndSortedResultRequestDto
{
public string Filter { get; set; }
}
}
RawFileDto.cs
namespace MyCompany.FileManagement.FileBlob
{
public class RawFileDto
{
public byte[] Bytes { get; set; }
public string Name { get; set; }
public string MimeType { get; set; }
public bool IsFileEmpty => Bytes == null || Bytes.Length == 0;
public RawFileDto() { }
public static RawFileDto EmptyResult()
{
return new RawFileDto() { Bytes = new byte[0] };
}
}
}
RenameInputDto.cs
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
namespace MyCompany.FileManagement.FileBlob
{
public class RenameInputDto : EntityDto<Guid>
{
[Required]
public string Name { get; set; }
[Required]
public string NewName { get; set; }
public bool IsFile { get; set; }
public string Path { get; set; }
}
}
(4)数据服务实现
这里需要使用类型化容器,类型化BLOB容器可以在程序中创建和管理多个容器,关于类型化容器可以参考官方文档,在Domain项目中添加BlobContainerName 属性装饰的类FilesContainer.cs
using Volo.Abp.BlobStoring;
namespace Shktiot.FileManagement
{
[BlobContainerName("files")]
public class FilesContainer
{
}
}
在FileManagementApplicationAutoMapperProfile.cs文件中添加Automapper映射配置:
public FileManagementApplicationAutoMapperProfile()
{
CreateMap<BlobFile, FileInfoDto>();
}
删除MyCompany.FileManagement.Application项目下Samples目录,添加Services目录,在其中添加个人文件服务类MyFileManageAppService.cs,内容如下:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using MyCompany.FileManagement.Entities;
using MyCompany.FileManagement.FileBlob;
using MyCompany.FileManagement.Permissions;
using Volo.Abp.Application.Dtos;
namespace MyCompany.FileManagement.Services
{
public class MyFileManageAppService : FileManagementAppService, IMyFileManageAppService
{
// 注入领域服务类
private readonly FileManagementManager _fileManagementManager;
public MyFileManageAppService(FileManagementManager fileManagementManager)
{
_fileManagementManager = fileManagementManager;
}
[Authorize(FileManagementPermissions.MyFiles.UploadFile)]
public async Task<FileUploadOutputDto> CreateAsync(FileUploadInputDto input)
{
var filePath = await _fileManagementManager.CreateAsync(input.Path, input.Name, input.Bytes, input.ContentType, CurrentUser.Id);
return new FileUploadOutputDto { WebUrl = filePath, Name = input.Name };
}
[Authorize(FileManagementPermissions.MyFiles.CreateDir)]
public async Task<string> CreateDirectoryAsync(CreateDirInputDto input)
{
var created = await _fileManagementManager.CreateDirectoryAsync(input.Path, input.Name, CurrentUser.Id);
return created;
}
[Authorize(FileManagementPermissions.MyFiles.Delete)]
public Task DeleteDirAsync(Guid id)
{
return _fileManagementManager.DeleteDirAsync(id);
}
[Authorize(FileManagementPermissions.MyFiles.Delete)]
public Task DeleteFileAsync(Guid id)
{
return _fileManagementManager.DeleteFileAsync(id);
}
[Authorize(FileManagementPermissions.MyFiles.Default)]
public async Task<RawFileDto> GetAsync(Guid id)
{
var file = await _fileManagementManager.GetBytesAsync(id);
var fileInfo = await _fileManagementManager.GetFileInfoAsync(id);
return new RawFileDto { Bytes = file, MimeType = fileInfo.MimeType, Name = fileInfo.Name };
}
[Authorize(FileManagementPermissions.MyFiles.Default)]
public async Task<PagedResultDto<FileInfoDto>> GetListAsync(string path, GetDirectoryListInput input)
{
var count = await _fileManagementManager.GetCountAsync(path, input.Filter, CurrentUser.Id);
var list = await _fileManagementManager.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, path, input.Filter, CurrentUser.Id);
return new PagedResultDto<FileInfoDto>(
count,
ObjectMapper.Map<List<BlobFile>, List<FileInfoDto>>(list)
);
}
[Authorize(FileManagementPermissions.MyFiles.Rename)]
public async Task RenameAsync(RenameInputDto input)
{
await _fileManagementManager.RenameAsync(input.Id, input.NewName);
}
}
}
继续添加公共文件服务类PublicFileManageAppService.cs,区别仅是领域方法调用时不传入当前用户的Id
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using MyCompany.FileManagement.Entities;
using MyCompany.FileManagement.FileBlob;
using MyCompany.FileManagement.Permissions;
using Volo.Abp.Application.Dtos;
namespace MyCompany.FileManagement.Services
{
/// <summary>
/// 与个人文件服务区别仅是领域方法调用时不传入当前用户的Id
/// </summary>
public class PublicFileManageAppService : FileManagementAppService, IPublicFileManageAppService
{
// 注入领域服务类
private readonly FileManagementManager _fileManagementManager;
public PublicFileManageAppService(FileManagementManager fileManagementManager)
{
_fileManagementManager = fileManagementManager;
}
[Authorize(FileManagementPermissions.PublicFiles.UploadFile)]
public async Task<FileUploadOutputDto> CreateAsync(FileUploadInputDto input)
{
var filePath = await _fileManagementManager.CreateAsync(input.Path, input.Name, input.Bytes, input.ContentType);
return new FileUploadOutputDto { WebUrl = filePath, Name = input.Name };
}
[Authorize(FileManagementPermissions.PublicFiles.CreateDir)]
public async Task<string> CreateDirectoryAsync(CreateDirInputDto input)
{
var created = await _fileManagementManager.CreateDirectoryAsync(input.Path, input.Name);
return created;
}
[Authorize(FileManagementPermissions.PublicFiles.Delete)]
public Task DeleteDirAsync(Guid id)
{
return _fileManagementManager.DeleteDirAsync(id);
}
[Authorize(FileManagementPermissions.PublicFiles.Delete)]
public Task DeleteFileAsync(Guid id)
{
return _fileManagementManager.DeleteFileAsync(id);
}
[Authorize(FileManagementPermissions.PublicFiles.Default)]
public async Task<RawFileDto> GetAsync(Guid id)
{
var file = await _fileManagementManager.GetBytesAsync(id);
var fileInfo = await _fileManagementManager.GetFileInfoAsync(id);
return new RawFileDto { Bytes = file, MimeType = fileInfo.MimeType, Name = fileInfo.Name };
}
[Authorize(FileManagementPermissions.PublicFiles.Default)]
public async Task<PagedResultDto<FileInfoDto>> GetListAsync(string path, GetDirectoryListInput input)
{
var count = await _fileManagementManager.GetCountAsync(path, input.Filter);
var list = await _fileManagementManager.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, path, input.Filter);
return new PagedResultDto<FileInfoDto>(
count,
ObjectMapper.Map<List<BlobFile>, List<FileInfoDto>>(list)
);
}
[Authorize(FileManagementPermissions.PublicFiles.Rename)]
public async Task RenameAsync(RenameInputDto input)
{
await _fileManagementManager.RenameAsync(input.Id, input.NewName);
}
}
}
(5)添加Webapi 控制器
双击MyCompany.FileManagement.HttpApi项目,添加以下引用:
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.1.4" />
...
</ItemGroup>
删除MyCompany.FileManagement.HttpApi项目中Sample目录,添加文件管理基类控制器BlobFileBaseController.cs,内容如下:
using MyCompany.FileManagement.FileBlob;
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
namespace MyCompany.FileManagement
{
public class BlobFileBaseController<TAppService>: FileManagementController where TAppService : IBlobFileManageAppService
{
protected readonly TAppService _fileAppService;
public BlobFileBaseController(TAppService fileAppService)
{
_fileAppService = fileAppService;
}
[HttpGet]
[Route("{id}")]
public Task<RawFileDto> GetAsync(Guid id)
{
return _fileAppService.GetAsync(id);
}
[RemoteService(false)]
[SwaggerResponse(200, type: typeof(FileContentResult))]
[ProducesResponseType(typeof(FileContentResult), 200)]
[HttpGet]
[Route("www/{id}")]
public async Task<FileResult> GetForWebAsync(Guid id)
{
var file = await _fileAppService.GetAsync(id);
return File(file.Bytes, file.MimeType, file.Name);
}
[HttpGet]
public Task<PagedResultDto<FileInfoDto>> GetListAsync(string path, GetDirectoryListInput input)
{
return _fileAppService.GetListAsync(path, input);
}
[RemoteService(false)]
[HttpPost]
[Route("upload")]
public async Task<JsonResult> CreateAsync(string path, 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,
Path = path?.TrimEnd('/'),
ContentType = file.ContentType
});
return new JsonResult(result);
}
[HttpPost]
[Route("dir")]
public Task<string> CreateDirectoryAsync(CreateDirInputDto input)
{
return _fileAppService.CreateDirectoryAsync(input);
}
[HttpPut]
[Route("rename")]
public Task RenameAsync(RenameInputDto input)
{
return _fileAppService.RenameAsync(input);
}
[HttpDelete]
[Route("dir/{id}")]
public Task DeleteDirAsync(Guid id)
{
return _fileAppService.DeleteDirAsync(id);
}
[HttpDelete]
[Route("file/{id}")]
public Task DeleteFileAsync(Guid id)
{
return _fileAppService.DeleteFileAsync(id);
}
}
}
添加个人文件管理控制器类MyFilesController.cs
using MyCompany.FileManagement.FileBlob;
using Microsoft.AspNetCore.Mvc;
namespace MyCompany.FileManagement
{
[Route("api/file-management/my-files")]
public class MyFilesController : BlobFileBaseController<IMyFileManageAppService>
{
public MyFilesController(IMyFileManageAppService fileAppService) : base(fileAppService)
{
}
}
}
继续添加公共文件管理控制器类PublicFilesController.cs
using MyCompany.FileManagement.FileBlob;
using Microsoft.AspNetCore.Mvc;
namespace MyCompany.FileManagement
{
[Route("api/file-management/public-files")]
public class PublicFilesController : BlobFileBaseController<IPublicFileManageAppService>
{
public PublicFilesController(IPublicFileManageAppService fileAppService): base(fileAppService)
{
}
}
}
5、测试接口
用vscode打开filemanagement.angular目录,执行终端命令npm install 和npm start启动项目,在浏览器打开http://localhost:4200/,使用admin登录,在角色页面修改admin角色的权限,添加文件管理的所有权限(这里没有添加中文资源)
浏览器打开Swagger Api 界面https://localhost:44358/swagger/index.html,点击Authorize按钮,使用admin用户进行认证
然后找到上传文件的接口方法:
点击Try it out按钮,在file栏选择一个文件上传,然后点击Execute按钮,执行成功后结果如下:
浏览器登录Minio控制台,可以看到文件已写入minio的库中了
这一章主要介绍后端代码的实现,下一节将介绍angular前端的实现
本文源码:Abp Vnext中使用Minio打造文件管理模块
————————————————
版权声明:本文为CSDN博主「沝林」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/duanzilin/article/details/121921772
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2021-03-17 js 闭包详解一