在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. 智能电视APP鲜时光,如何应用AB测试打造极致的用户观看体验?

     更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群   数字技术的发展让智能电视普及率大幅提升,2023年智能电视的市场渗透率已超90%,与智能电视相匹配的各类应用 ...

  2. 火山引擎云原生数据仓库 ByteHouse 技术白皮书 V1.0(中)

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 近日,<火山引擎云原生数据仓库 ByteHouse 技术白皮书>正式发布.白皮书简述了 ByteHou ...

  3. Solon 1.6.29 发布,轻量级应用开发框架

    关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web.Data.Jo ...

  4. Jenkins Pipeline 流水线 - 拉代码(SVN) + Maven 编译打包

    Jenkins Pipeline 流水线 步骤 拉取SVN代码 -> Maven 构建 -> Docker 编译 -> 发布至阿里云仓库 -> K8S 更新 Jenkins插件 ...

  5. 【设计模式】分享 Java 开发中常用到的设计模式(一)

    分享 Java 开发中常用到的设计模式(一) 前言 不知道大家在开发的时候,有没有想过(遇到)这些问题: 大家都是按需要开发,都是一个职级的同事,为什么有些人的思路就很清晰,代码也很整洁.易懂:而自己 ...

  6. .NET Moq mock internal类型

    问题 Can not create proxy for type xxx because type xxx is not accessible. Make it public, or internal ...

  7. 3、springboot连接数据库

    系列导航 springBoot项目打jar包 1.springboot工程新建(单模块) 2.springboot创建多模块工程 3.springboot连接数据库 4.SpringBoot连接数据库 ...

  8. 三、docker容器的常用命令

    系列导航 一.docker入门(概念) 二.docker的安装和镜像管理 三.docker容器的常用命令 四.容器的网络访问 五.容器端口转发 六.docker数据卷 七.手动制作docker镜像 八 ...

  9. js滑动验证

    https://gitee.com/anji-plus/captcha AjCaptcha验证码: https://blog.csdn.net/zbchina2004/article/details/ ...

  10. Scan Synthesis Review

    Review scan replacement - 将normal DFF替换为mux gate DFF scan stitching - 将DFF连接起来 scan的作用:将测试困难的时序逻辑转变为 ...