在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前端的实现