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项目,添加如下引用:


"Volo.Abp.BlobStoring" Version="5.0.0-rc.1" />
"Volo.Abp.BlobStoring.Minio" Version="5.0.0-rc.1" />
...
...


打开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(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, 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
{
/// 
/// 文件管理数据仓库接口
/// 
public interface IBlobFileRepository : IBasicRepository
{
// 根据条件获取分页记录
Task> 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> 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 _blobContainer;
// 注入文件管理数据仓库接口
private readonly IBlobFileRepository _fileRepository;

public FileManagementManager(IBlobContainer blobContainer, IBlobFileRepository fileRepository)
{
_blobContainer = blobContainer;
_fileRepository = fileRepository;
}

/// 
/// 上传文件,userId为空时是公共文件
/// 
/// 
/// 
/// 
/// 
/// 
/// 
/// 
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 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> 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 BlobFiles { get; }
}
在FileManagementDbContext.cs中添加BlobFile属性实现

public class FileManagementDbContext : AbpDbContext, IFileManagementDbContext
{
public DbSet 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(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,
IBlobFileRepository
{

public BlobFileRepository(
IDbContextProvider dbContextProvider)
: base(dbContextProvider)
{
}

public async Task> 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> 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
{
/// 
/// 获取文件
/// 
/// 
/// 
Task GetAsync(Guid id);
/// 
/// 创建目录
/// 
/// 
/// 
Task<string> CreateDirectoryAsync(CreateDirInputDto input);
/// 
/// 重命名
/// 
/// 
/// 
Task RenameAsync(RenameInputDto input);
/// 
/// 上传创建文件
/// 
/// 
/// 
Task CreateAsync(FileUploadInputDto input);
/// 
/// 获取文件列表
/// 
/// 
/// 
/// 
Task> GetListAsync(string path, GetDirectoryListInput input);
/// 
/// 删除目录
/// 
/// 
/// 
Task DeleteDirAsync(Guid id);
/// 
/// 删除文件
/// 
/// 
/// 
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
{
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
{
[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();
}
删除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 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 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 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> 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(
count,
ObjectMapper.Map, List>(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
{
///


/// 与个人文件服务区别仅是领域方法调用时不传入当前用户的Id
///

public class PublicFileManageAppService : FileManagementAppService, IPublicFileManageAppService
{
// 注入领域服务类
private readonly FileManagementManager _fileManagementManager;

public PublicFileManageAppService(FileManagementManager fileManagementManager)
{
_fileManagementManager = fileManagementManager;
}

[Authorize(FileManagementPermissions.PublicFiles.UploadFile)]
public async Task 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 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 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> 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(
count,
ObjectMapper.Map, List>(list)
);
}
[Authorize(FileManagementPermissions.PublicFiles.Rename)]
public async Task RenameAsync(RenameInputDto input)
{
await _fileManagementManager.RenameAsync(input.Id, input.NewName);
}
}
}
(5)添加Webapi 控制器

双击MyCompany.FileManagement.HttpApi项目,添加以下引用:



...

删除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: FileManagementController where TAppService : IBlobFileManageAppService
{
protected readonly TAppService _fileAppService;

public BlobFileBaseController(TAppService fileAppService)
{
_fileAppService = fileAppService;
}

[HttpGet]
[Route("{id}")]
public Task 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 GetForWebAsync(Guid id)
{
var file = await _fileAppService.GetAsync(id);
return File(file.Bytes, file.MimeType, file.Name);
}

[HttpGet]
public Task> GetListAsync(string path, GetDirectoryListInput input)
{
return _fileAppService.GetListAsync(path, input);
}

[RemoteService(false)]
[HttpPost]
[Route("upload")]
public async Task 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 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
{
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
{
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

相关