在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

miniIO系列文章03---abpvext中集成的更多相关文章

  1. Java Web学习系列——Maven Web项目中集成使用Spring、MyBatis实现对MySQL的数据访问

    本篇内容还是建立在上一篇Java Web学习系列——Maven Web项目中集成使用Spring基础之上,对之前的Maven Web项目进行升级改造,实现对MySQL的数据访问. 添加依赖Jar包 这 ...

  2. Java Web学习系列——Maven Web项目中集成使用Spring

    参考Java Web学习系列——创建基于Maven的Web项目一文,创建一个名为LockMIS的Maven Web项目. 添加依赖Jar包 推荐在http://mvnrepository.com/.h ...

  3. ionic+vue+capacitor系列笔记--02项目中集成Capacitor,添加android,ios平台,真机运行项目

    Capacitor是什么? Capacitor是由ionic团队开发的一款跨平台移动应用构建工具,可轻让我们轻松的构建Android.iOS.Electron和Web应用程序. Capacitor是A ...

  4. IT软件人员的技术学习内容(写给技术迷茫中的你) - 项目管理系列文章

    前面笔者曾经写过一篇关于IT从业者的职业道路文章(见笔者文:IT从业者的职业道路(从程序员到部门经理) - 项目管理系列文章).然后有读者提建议说写写技术方面的路线,所以就有了本文.本文从初学者到思想 ...

  5. Bing Maps进阶系列八:在Bing Maps中集成OpenStreetMap地图

    Bing Maps进阶系列八:在Bing Maps中集成OpenStreetMap地图 OSM(OpenStreetMap-开放街道地图)服务就是一种发布自己地图数据图片为服务的一种实现类型,开放街道 ...

  6. [译]MVC网站教程(四):MVC4网站中集成jqGrid表格插件(系列完结)

    目录 1.   介绍 2.   软件环境 3.   在运行示例代码之前(源代码 + 示例登陆帐号) 4.         jqGrid和AJAX 5.         GridSettings 6.  ...

  7. Linux 系统化学习系列文章总目录(持续更新中)

    本页内容都是本人系统化学习Linux 时整理出来的.这些文章中,绝大多数命令类内容都是翻译.整理man或info文档总结出来的,所以相对都比较完整. 本人的写作方式.风格也可能会让朋友一看就恶心到直接 ...

  8. 痞子衡嵌入式:深扒i.MXRTxxx系列ROM中集成的串行NOR Flash启动SW Reset功能及其应用场合

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRTxxx系列ROM中集成的串行NOR Flash启动SW Reset功能及其应用场合. 在串行 NOR Flash 热启动过程 ...

  9. Grafana 系列文章(六):Grafana Explore 中的日志

    ️URL: https://grafana.com/docs/grafana/latest/explore/logs-integration/#labels-and-detected-fields D ...

  10. Grafana 系列文章(十一):Loki 中的标签如何使日志查询更快更方便

    ️URL: https://grafana.com/blog/2020/04/21/how-labels-in-loki-can-make-log-queries-faster-and-easier/ ...

随机推荐

  1. PPT 求职应聘:如何制作人物介绍

    PPT 求职应聘:如何制作人物介绍 单人展示

  2. Jenkins Pipeline 多分支流水线 Input length = 1

    Jenkins 多分支流水线 构建过程中报错. [Pipeline] // node [Pipeline] End of Pipeline java.nio.charset.MalformedInpu ...

  3. 熔断、限流、降级 —— SpringCloud Alibaba Sentinel

    Sentinel 简介 Sentinel 是阿里中间件团队开源的,面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流.流量整形.熔断降级.系统负载保护.热点防护等多个维度来帮助开发者保 ...

  4. 机器人多目标包围问题(MECA)新算法:基于关系图深度强化学习

    摘要:中科院自动化所蒲志强教授团队,提出一种基于关系图的深度强化学习方法,应用于多目标避碰包围问题(MECA),使用NOKOV度量动作捕捉系统获取多机器人位置信息,验证了方法的有效性和适应性.研究成果 ...

  5. 如何利用CCXT交易数字货币合约

    更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. 对于币圈量化老司机来说,相信或多或少都有接触过ccxt这个接口,ccxt为我们提供了多交易所统一的标准格 ...

  6. hyper-v虚拟机中ubuntu连不上网络的解决办法

    首先重启下hyper-v的服务,看下情况: 1.检查hyper-v相关的服务有没有开启 2.如果开启了服务,unbuntu仍然不能连网,则在ubtuntu中进行接下来的步骤: 2.1 设置网络连接为N ...

  7. WPF 组织机构摄像机树 全量加载 大数据量 分页加载摄像机节点

    WPF 组织机构摄像机树 全量加载 大数据量 分页加载摄像机节点 完整代码: https://gitee.com/s0611163/WpfTreeDemo 性能: 8000组织机构20万摄像机,全量加 ...

  8. Codeforces Round #665 (Div. 2) A - D题题解

    成功拼手速提前过了AC两题,估计因为这个原因排名挺高的,B题晚上做的时候没绕出来,wa4发... 1401A - Distance and Axis 如果 \(n\) 小 于 \(k\) ,则必须将\ ...

  9. vue 中安装使用jquery

    https://www.cnblogs.com/tcz1018/p/13970479.html

  10. freeswitch APR库内存池

    概述 freeswitch的核心源代码是基于apr库开发的,在不同的系统上有很好的移植性. apr库中的大部分API都需要依赖于内存池,使用内存池简化内存管理,提高内存分配效率,减少内存操作中出错的概 ...