diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/FileShareDto.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/FileShareDto.cs new file mode 100644 index 000000000..e02ecb14b --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/FileShareDto.cs @@ -0,0 +1,32 @@ +using System; + +namespace LINGYUN.Abp.OssManagement +{ + public class FileShareDto + { + public string Url { get; set; } + public int MaxAccessCount { get; set; } + public DateTime? ExpirationTime { get; set; } + } + + public class MyFileShareDto + { + public string Name { get; set; } + + public string Path { get; set; } + + public string[] Roles { get; set; } + + public string[] Users { get; set; } + + public string MD5 { get; set; } + + public string Url { get; set; } + + public int AccessCount { get; set; } + + public int MaxAccessCount { get; set; } + + public DateTime ExpirationTime { get; set; } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/FileShareInput.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/FileShareInput.cs new file mode 100644 index 000000000..b0bf5e7d4 --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/FileShareInput.cs @@ -0,0 +1,21 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace LINGYUN.Abp.OssManagement +{ + public class FileShareInput + { + [Required] + public string Name { get; set; } + + public string Path { get; set; } + + public int MaxAccessCount { get; set; } + + public DateTime? ExpirationTime { get; set; } + + public string[] Roles { get; set; } + + public string[] Users { get; set; } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/GetFileShareDto.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/GetFileShareDto.cs new file mode 100644 index 000000000..abdb87f2d --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/GetFileShareDto.cs @@ -0,0 +1,17 @@ +using System.IO; + +namespace LINGYUN.Abp.OssManagement +{ + public class GetFileShareDto + { + public string Name { get; set; } + public Stream Content { get; set; } + public GetFileShareDto( + string name, + Stream content = null) + { + Name = name; + Content = content ?? Stream.Null; + } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/GetFilesInput.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/GetFilesInput.cs new file mode 100644 index 000000000..964c5e380 --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/GetFilesInput.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.OssManagement +{ + public class GetFilesInput: LimitedResultRequestDto + { + public string Filter { get; set; } + public string Path { get; set; } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IFileAppService.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IFileAppService.cs index 5bf9a133f..f2cecf008 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IFileAppService.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IFileAppService.cs @@ -1,5 +1,6 @@ using System.IO; using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; namespace LINGYUN.Abp.OssManagement @@ -10,6 +11,10 @@ namespace LINGYUN.Abp.OssManagement Task GetAsync(GetPublicFileInput input); + Task> GetListAsync(GetFilesInput input); + Task UploadAsync(UploadFileChunkInput input); + + Task DeleteAsync(GetPublicFileInput input); } } diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IPrivateFileAppService.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IPrivateFileAppService.cs index 6487dde80..d11706949 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IPrivateFileAppService.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IPrivateFileAppService.cs @@ -1,6 +1,12 @@ -namespace LINGYUN.Abp.OssManagement +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; + +namespace LINGYUN.Abp.OssManagement { public interface IPrivateFileAppService : IFileAppService { + Task ShareAsync(FileShareInput input); + + Task> GetShareListAsync(); } } diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IShareFileAppService.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IShareFileAppService.cs new file mode 100644 index 000000000..60f3e79f7 --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application.Contracts/LINGYUN/Abp/OssManagement/IShareFileAppService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Application.Services; + +namespace LINGYUN.Abp.OssManagement +{ + public interface IShareFileAppService : IApplicationService + { + Task GetAsync(string url); + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/FileAppServiceBase.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/FileAppServiceBase.cs index a9186c891..95502e0a9 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/FileAppServiceBase.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/FileAppServiceBase.cs @@ -1,33 +1,35 @@ using LINGYUN.Abp.Features.LimitValidation; using LINGYUN.Abp.OssManagement.Features; using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using System.Web; +using Volo.Abp.Application.Dtos; using Volo.Abp.Features; namespace LINGYUN.Abp.OssManagement { public abstract class FileAppServiceBase : OssManagementApplicationServiceBase, IFileAppService { - private readonly IFileUploader _fileUploader; - private readonly IFileValidater _fileValidater; - private readonly IOssContainerFactory _ossContainerFactory; + protected IFileUploader FileUploader { get; } + protected IFileValidater FileValidater { get; } + protected IOssContainerFactory OssContainerFactory { get; } protected FileAppServiceBase( IFileUploader fileUploader, IFileValidater fileValidater, IOssContainerFactory ossContainerFactory) { - _fileUploader = fileUploader; - _fileValidater = fileValidater; - _ossContainerFactory = ossContainerFactory; + FileUploader = fileUploader; + FileValidater = fileValidater; + OssContainerFactory = ossContainerFactory; } [RequiresFeature(AbpOssManagementFeatureNames.OssObject.UploadFile)] public virtual async Task UploadAsync(UploadFileChunkInput input) { - await _fileUploader.UploadAsync( + await FileUploader.UploadAsync( new UploadFileChunkInput { Bucket = GetCurrentBucket(), @@ -49,13 +51,13 @@ namespace LINGYUN.Abp.OssManagement LimitPolicy.Month)] public virtual async Task UploadAsync(UploadFileInput input) { - await _fileValidater.ValidationAsync(new UploadFile + await FileValidater.ValidationAsync(new UploadFile { TotalSize = input.Content.Length, FileName = input.Object }); - var oss = _ossContainerFactory.Create(); + var oss = OssContainerFactory.Create(); var createOssObjectRequest = new CreateOssObjectRequest( GetCurrentBucket(), @@ -71,6 +73,19 @@ namespace LINGYUN.Abp.OssManagement return ObjectMapper.Map(ossObject); } + public virtual async Task> GetListAsync(GetFilesInput input) + { + var ossContainer = OssContainerFactory.Create(); + var response = await ossContainer.GetObjectsAsync( + GetCurrentBucket(), + GetCurrentPath(HttpUtility.UrlDecode(input.Path)), + skipCount: 0, + maxResultCount: input.MaxResultCount); + + return new ListResultDto( + ObjectMapper.Map, List>(response.Objects)); + } + [RequiresFeature(AbpOssManagementFeatureNames.OssObject.DownloadFile)] [RequiresLimitFeature( AbpOssManagementFeatureNames.OssObject.DownloadLimit, @@ -88,12 +103,22 @@ namespace LINGYUN.Abp.OssManagement MD5 = true, }; - var ossContainer = _ossContainerFactory.Create(); + var ossContainer = OssContainerFactory.Create(); var ossObject = await ossContainer.GetObjectAsync(ossObjectRequest); return ossObject.Content; } + public virtual async Task DeleteAsync(GetPublicFileInput input) + { + var ossContainer = OssContainerFactory.Create(); + + await ossContainer.DeleteObjectAsync( + GetCurrentBucket(), + HttpUtility.UrlDecode(input.Name), + GetCurrentPath(HttpUtility.UrlDecode(input.Path))); + } + protected virtual string GetCurrentBucket() { throw new System.NotImplementedException(); diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/FileShareCacheItem.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/FileShareCacheItem.cs new file mode 100644 index 000000000..9417dea4b --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/FileShareCacheItem.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LINGYUN.Abp.OssManagement +{ + [Serializable] + public class MyFileShareCacheItem + { + private const string CacheKeyFormat = "ui:{0}"; + + public List Items { get; set; } + public MyFileShareCacheItem() { } + public MyFileShareCacheItem(List items) + { + Items = items ?? new List(); + } + + public static string CalculateCacheKey(Guid userId) + { + return string.Format(CacheKeyFormat, userId.ToString("N")); + } + + public DateTime? GetLastExpirationTime() + { + if (!Items.Any()) + { + return null; + } + + return Items + .OrderByDescending(item => item.ExpirationTime) + .Select(item => item.ExpirationTime) + .FirstOrDefault(); + } + } + + [Serializable] + public class FileShareCacheItem + { + private const string CacheKeyFormat = "url:{0}"; + + public string Bucket { get; set; } + + public string Name { get; set; } + + public string Path { get; set; } + + public string[] Roles { get; set; } + + public string[] Users { get; set; } + + public string MD5 { get; set; } + + public string Url { get; set; } + + public int AccessCount { get; set; } + + public int MaxAccessCount { get; set; } + + public DateTime ExpirationTime { get; set; } + + public Guid UserId { get; set; } + + public FileShareCacheItem() { } + + public FileShareCacheItem( + Guid userId, + string bucket, + string name, + string path, + string md5, + string url, + DateTime expirationTime, + string[] roles = null, + string[] users = null, + int maxAccessCount = 0) + { + UserId = userId; + Bucket = bucket; + Name = name; + Path = path; + MD5 = md5; + Url = url; + ExpirationTime = expirationTime; + Roles = roles ?? new string[0]; + Users = users ?? new string[0]; + MaxAccessCount = maxAccessCount; + } + + public static string CalculateCacheKey(string shareUrl) + { + return string.Format(CacheKeyFormat, shareUrl); + } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationAutoMapperProfile.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationAutoMapperProfile.cs index 47441dd87..8777051a9 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationAutoMapperProfile.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/OssManagementApplicationAutoMapperProfile.cs @@ -1,17 +1,19 @@ -using AutoMapper; - -namespace LINGYUN.Abp.OssManagement -{ - public class OssManagementApplicationAutoMapperProfile : Profile - { - public OssManagementApplicationAutoMapperProfile() - { - CreateMap(); - CreateMap() - .ForMember(dto => dto.Path, map => map.MapFrom(src => src.Prefix)); - - CreateMap(); - CreateMap(); - } - } -} +using AutoMapper; + +namespace LINGYUN.Abp.OssManagement +{ + public class OssManagementApplicationAutoMapperProfile : Profile + { + public OssManagementApplicationAutoMapperProfile() + { + CreateMap(); + CreateMap() + .ForMember(dto => dto.Path, map => map.MapFrom(src => src.Prefix)); + + CreateMap(); + CreateMap(); + + CreateMap(); + } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PrivateFileAppService.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PrivateFileAppService.cs index d97059e73..8588ef3f3 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PrivateFileAppService.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PrivateFileAppService.cs @@ -1,4 +1,13 @@ using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Caching.Distributed; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using System.Web; +using Volo.Abp.Application.Dtos; +using Volo.Abp.Caching; +using Volo.Abp.IO; using Volo.Abp.Users; namespace LINGYUN.Abp.OssManagement @@ -13,12 +22,154 @@ namespace LINGYUN.Abp.OssManagement //[RemoteService(IsMetadataEnabled = false)] public class PrivateFileAppService : FileAppServiceBase, IPrivateFileAppService { + private readonly IDistributedCache _shareCache; + private readonly IDistributedCache _myShareCache; public PrivateFileAppService( + IDistributedCache shareCache, + IDistributedCache myShareCache, IFileUploader fileUploader, IFileValidater fileValidater, IOssContainerFactory ossContainerFactory) : base(fileUploader, fileValidater, ossContainerFactory) { + _shareCache = shareCache; + _myShareCache = myShareCache; + } + + [Authorize] + public override async Task GetAsync(GetPublicFileInput input) + { + return await base.GetAsync(input); + } + + [Authorize] + public override async Task> GetListAsync(GetFilesInput input) + { + return await base.GetListAsync(input); + } + + [Authorize] + public override async Task UploadAsync(UploadFileInput input) + { + return await base.UploadAsync(input); + } + + [Authorize] + public override async Task UploadAsync(UploadFileChunkInput input) + { + await base.UploadAsync(input); + } + + [Authorize] + public virtual async Task> GetShareListAsync() + { + var cacheKey = MyFileShareCacheItem.CalculateCacheKey(CurrentUser.GetId()); + var cacheItem = await _myShareCache.GetAsync(cacheKey); + if (cacheItem == null) + { + return new ListResultDto(); + } + + // 被动刷新用户共享缓存 + // 手动调用时清除一下应该被清理掉的缓存 + cacheItem.Items.RemoveAll(items => items.MaxAccessCount > 0 && items.AccessCount > items.MaxAccessCount); + cacheItem.Items.RemoveAll(items => items.ExpirationTime < Clock.Now); + + DistributedCacheEntryOptions cacheOptions = null; + var myShareCacheExpirationTime = cacheItem.GetLastExpirationTime(); + if (myShareCacheExpirationTime.HasValue) + { + cacheOptions = new DistributedCacheEntryOptions + { + AbsoluteExpiration = DateTimeOffset.Now.Add( + Clock.Normalize(myShareCacheExpirationTime.Value) - Clock.Now), + }; + } + await _myShareCache.SetAsync(cacheKey, cacheItem, cacheOptions); + + return new ListResultDto( + ObjectMapper.Map, List>(cacheItem.Items)); + } + + [Authorize] + public virtual async Task ShareAsync(FileShareInput input) + { + var ossObjectRequest = new GetOssObjectRequest( + GetCurrentBucket(), + // 需要处理特殊字符 + HttpUtility.UrlDecode(input.Name), + GetCurrentPath(HttpUtility.UrlDecode(input.Path))) + { + MD5 = true, + }; + var ossContainer = OssContainerFactory.Create(); + var ossObject = await ossContainer.GetObjectAsync(ossObjectRequest); + + var shareUrl = $"{GuidGenerator.Create():N}.{FileHelper.GetExtension(ossObject.Name)}"; + var cacheItem = new FileShareCacheItem( + CurrentUser.GetId(), + GetCurrentBucket(), + ossObject.Name, + ossObject.Prefix, + ossObject.MD5, + shareUrl, + input.ExpirationTime ?? Clock.Now.AddDays(7), + input.Roles, + input.Users, + input.MaxAccessCount); + + var cacheOptions = new DistributedCacheEntryOptions + { + // 不传递过期时间, 默认7天 + AbsoluteExpiration = DateTimeOffset.Now.Add( + Clock.Normalize(cacheItem.ExpirationTime) - Clock.Now), + }; + + await _shareCache.SetAsync( + FileShareCacheItem.CalculateCacheKey(shareUrl), + cacheItem, + cacheOptions); + + #region 当前用户共享缓存 + + // 被动刷新用户共享缓存 + var myShareCacheKey = MyFileShareCacheItem.CalculateCacheKey(CurrentUser.GetId()); + var myShareCacheItem = await _myShareCache.GetAsync(myShareCacheKey); + if (myShareCacheItem == null) + { + myShareCacheItem = new MyFileShareCacheItem( + new List() { cacheItem }); + } + else + { + myShareCacheItem.Items.Add(cacheItem); + } + DistributedCacheEntryOptions myShareCacheOptions = null; + var myShareCacheExpirationTime = myShareCacheItem.GetLastExpirationTime(); + if (myShareCacheExpirationTime.HasValue) + { + myShareCacheOptions = new DistributedCacheEntryOptions + { + AbsoluteExpiration = DateTimeOffset.Now.Add( + Clock.Normalize(myShareCacheExpirationTime.Value) - Clock.Now), + }; + } + await _myShareCache.SetAsync(myShareCacheKey, myShareCacheItem, myShareCacheOptions); + + #endregion + + return new FileShareDto + { + Url = shareUrl, + MaxAccessCount = input.MaxAccessCount, + ExpirationTime = input.ExpirationTime, + }; + } + + [Authorize] + public override async Task DeleteAsync(GetPublicFileInput input) + { + await base.DeleteAsync(input); } protected override string GetCurrentBucket() diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PublicFileAppService.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PublicFileAppService.cs index 76f063a39..bb42b70a9 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PublicFileAppService.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/PublicFileAppService.cs @@ -1,11 +1,15 @@ using LINGYUN.Abp.Features.LimitValidation; using LINGYUN.Abp.OssManagement.Features; +using LINGYUN.Abp.OssManagement.Permissions; +using Microsoft.AspNetCore.Authorization; using System.IO; using System.Threading.Tasks; +using Volo.Abp.Application.Dtos; using Volo.Abp.Features; namespace LINGYUN.Abp.OssManagement { + [AllowAnonymous] public class PublicFileAppService : FileAppServiceBase, IPublicFileAppService { public PublicFileAppService( @@ -16,38 +20,54 @@ namespace LINGYUN.Abp.OssManagement { } - [RequiresFeature( - AbpOssManagementFeatureNames.PublicAccess, - AbpOssManagementFeatureNames.OssObject.UploadFile, - RequiresAll = true)] + [Authorize(AbpOssManagementPermissions.OssObject.Delete)] + public override async Task DeleteAsync(GetPublicFileInput input) + { + await CheckPublicAccessAsync(); + await CheckPolicyAsync(AbpOssManagementPermissions.OssObject.Delete); + + await base.DeleteAsync(input); + } + public override async Task UploadAsync(UploadFileChunkInput input) { + await CheckPublicAccessAsync(); + await FeatureChecker.CheckEnabledAsync(AbpOssManagementFeatureNames.OssObject.UploadFile); + await base.UploadAsync(input); } - [RequiresFeature( - AbpOssManagementFeatureNames.PublicAccess, - AbpOssManagementFeatureNames.OssObject.UploadFile, - RequiresAll = true)] [RequiresLimitFeature( AbpOssManagementFeatureNames.OssObject.UploadLimit, AbpOssManagementFeatureNames.OssObject.UploadInterval, LimitPolicy.Month)] public override async Task UploadAsync(UploadFileInput input) { + await CheckPublicAccessAsync(); + await FeatureChecker.CheckEnabledAsync(AbpOssManagementFeatureNames.OssObject.UploadFile); + + // 公共目录不允许覆盖 + input.Overwrite = false; + return await base.UploadAsync(input); } - [RequiresFeature( - AbpOssManagementFeatureNames.PublicAccess, - AbpOssManagementFeatureNames.OssObject.DownloadFile, - RequiresAll = true)] + public override async Task> GetListAsync(GetFilesInput input) + { + await CheckPublicAccessAsync(); + + return await base.GetListAsync(input); + } + [RequiresLimitFeature( AbpOssManagementFeatureNames.OssObject.DownloadLimit, AbpOssManagementFeatureNames.OssObject.DownloadInterval, LimitPolicy.Month)] public override async Task GetAsync(GetPublicFileInput input) { + await CheckPublicAccessAsync(); + await FeatureChecker.CheckEnabledAsync(AbpOssManagementFeatureNames.OssObject.DownloadFile); + return await base.GetAsync(input); } @@ -55,5 +75,13 @@ namespace LINGYUN.Abp.OssManagement { return "public"; } + + protected virtual async Task CheckPublicAccessAsync() + { + if (!CurrentUser.IsAuthenticated) + { + await FeatureChecker.CheckEnabledAsync(AbpOssManagementFeatureNames.PublicAccess); + } + } } } diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/ShareFileAppService.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/ShareFileAppService.cs new file mode 100644 index 000000000..5939ed0f9 --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Application/LINGYUN/Abp/OssManagement/ShareFileAppService.cs @@ -0,0 +1,119 @@ +using Microsoft.Extensions.Caching.Distributed; +using System; +using System.Linq; +using System.Threading.Tasks; +using Volo.Abp.Caching; + +namespace LINGYUN.Abp.OssManagement +{ + public class ShareFileAppService : OssManagementApplicationServiceBase, IShareFileAppService + { + private readonly IDistributedCache _shareCache; + private readonly IDistributedCache _myShareCache; + private readonly IOssContainerFactory _ossContainerFactory; + + public ShareFileAppService( + IDistributedCache shareCache, + IDistributedCache myShareCache, + IOssContainerFactory ossContainerFactory) + { + _shareCache = shareCache; + _myShareCache = myShareCache; + _ossContainerFactory = ossContainerFactory; + } + + public virtual async Task GetAsync(string url) + { + var cacheKey = FileShareCacheItem.CalculateCacheKey(url); + var cacheItem = await _shareCache.GetAsync(cacheKey); + if (cacheItem == null) + { + return new GetFileShareDto(url); + } + + // 最大访问次数 + cacheItem.AccessCount += 1; + + // 被动刷新用户共享缓存 + await RefreshUserShareAsync(cacheItem); + + if (cacheItem.MaxAccessCount > 0 && cacheItem.AccessCount > cacheItem.MaxAccessCount) + { + await _shareCache.RemoveAsync(cacheKey); + + return new GetFileShareDto(url); + } + + // 共享用户 + if (cacheItem.Users != null && cacheItem.Users.Any()) + { + if (cacheItem.Users.Any((userName) => !userName.Equals(CurrentUser.UserName))) + { + return new GetFileShareDto(url); + } + } + + // 共享角色 + if (cacheItem.Roles != null && cacheItem.Roles.Any()) + { + if (cacheItem.Roles.Any((role) => !CurrentUser.Roles.Contains(role))) + { + return new GetFileShareDto(url); + } + } + + var ossObjectRequest = new GetOssObjectRequest( + cacheItem.Bucket, + cacheItem.Name, + cacheItem.Path) + { + MD5 = true, + }; + + var ossContainer = _ossContainerFactory.Create(); + var ossObject = await ossContainer.GetObjectAsync(ossObjectRequest); + + var cacheOptions = new DistributedCacheEntryOptions + { + // 不传递过期时间, 默认7天 + AbsoluteExpiration = DateTimeOffset.Now.Add( + Clock.Normalize(cacheItem.ExpirationTime) - Clock.Now), + }; + // 改变共享次数 + await _shareCache.SetAsync( + cacheKey, + cacheItem, + cacheOptions); + + return new GetFileShareDto(ossObject.Name, ossObject.Content); + } + + protected virtual async Task RefreshUserShareAsync(FileShareCacheItem shareCacheItem) + { + // 清除当前用户共享缓存 + var myShareCacheKey = MyFileShareCacheItem.CalculateCacheKey(shareCacheItem.UserId); + var myShareCacheItem = await _myShareCache.GetAsync(myShareCacheKey); + if (myShareCacheItem != null) + { + myShareCacheItem.Items.RemoveAll(item => item.Url.Equals(shareCacheItem.Url)); + if (shareCacheItem.MaxAccessCount == 0 || shareCacheItem.AccessCount < shareCacheItem.MaxAccessCount) + { + myShareCacheItem.Items.Add(shareCacheItem); + } + + DistributedCacheEntryOptions myShareCacheOptions = null; + var myShareCacheExpirationTime = myShareCacheItem.GetLastExpirationTime(); + if (myShareCacheExpirationTime.HasValue) + { + myShareCacheOptions = new DistributedCacheEntryOptions + { + AbsoluteExpiration = DateTimeOffset.Now.Add( + Clock.Normalize(myShareCacheExpirationTime.Value) - Clock.Now), + }; + } + + await _myShareCache.SetAsync(myShareCacheKey, myShareCacheItem, myShareCacheOptions); + } + } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Settings/AbpOssManagementSettingDefinitionProvider.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Settings/AbpOssManagementSettingDefinitionProvider.cs index 6f01001a1..364f0806f 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Settings/AbpOssManagementSettingDefinitionProvider.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.Domain.Shared/LINGYUN/Abp/OssManagement/Settings/AbpOssManagementSettingDefinitionProvider.cs @@ -1,44 +1,44 @@ -using LINGYUN.Abp.OssManagement.Localization; -using Volo.Abp.Localization; -using Volo.Abp.Settings; - -namespace LINGYUN.Abp.OssManagement.Settings -{ - public class AbpOssManagementSettingDefinitionProvider : SettingDefinitionProvider - { - public override void Define(ISettingDefinitionContext context) - { - context.Add(CreateFileSystemSettings()); - } - - protected SettingDefinition[] CreateFileSystemSettings() - { - return new SettingDefinition[] - { - new SettingDefinition( - name: AbpOssManagementSettingNames.FileLimitLength, - defaultValue: AbpOssManagementSettingNames.DefaultFileLimitLength.ToString(), - displayName: L("DisplayName:FileLimitLength"), - description: L("Description:FileLimitLength"), - isVisibleToClients: true) - .WithProviders( - GlobalSettingValueProvider.ProviderName, - TenantSettingValueProvider.ProviderName), - new SettingDefinition( - name: AbpOssManagementSettingNames.AllowFileExtensions, - defaultValue: AbpOssManagementSettingNames.DefaultAllowFileExtensions, - displayName: L("DisplayName:AllowFileExtensions"), - description: L("Description:AllowFileExtensions"), - isVisibleToClients: true) - .WithProviders( - GlobalSettingValueProvider.ProviderName, - TenantSettingValueProvider.ProviderName), - }; - } - - protected LocalizableString L(string name) - { - return LocalizableString.Create(name); - } - } -} +using LINGYUN.Abp.OssManagement.Localization; +using Volo.Abp.Localization; +using Volo.Abp.Settings; + +namespace LINGYUN.Abp.OssManagement.Settings +{ + public class AbpOssManagementSettingDefinitionProvider : SettingDefinitionProvider + { + public override void Define(ISettingDefinitionContext context) + { + context.Add(CreateFileSystemSettings()); + } + + protected SettingDefinition[] CreateFileSystemSettings() + { + return new SettingDefinition[] + { + new SettingDefinition( + name: AbpOssManagementSettingNames.FileLimitLength, + defaultValue: AbpOssManagementSettingNames.DefaultFileLimitLength.ToString(), + displayName: L("DisplayName:FileLimitLength"), + description: L("Description:FileLimitLength"), + isVisibleToClients: true) + .WithProviders( + GlobalSettingValueProvider.ProviderName, + TenantSettingValueProvider.ProviderName), + new SettingDefinition( + name: AbpOssManagementSettingNames.AllowFileExtensions, + defaultValue: AbpOssManagementSettingNames.DefaultAllowFileExtensions, + displayName: L("DisplayName:AllowFileExtensions"), + description: L("Description:AllowFileExtensions"), + isVisibleToClients: true) + .WithProviders( + GlobalSettingValueProvider.ProviderName, + TenantSettingValueProvider.ProviderName), + }; + } + + protected LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PrivateFilesController.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PrivateFilesController.cs index 169707066..6e3f86e35 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PrivateFilesController.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PrivateFilesController.cs @@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; using Volo.Abp; +using Volo.Abp.Application.Dtos; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Http; using Volo.Abp.Validation; @@ -68,6 +69,13 @@ namespace LINGYUN.Abp.OssManagement return await _privateFileAppService.UploadAsync(createOssObjectInput); } + [HttpGet] + [Route("search")] + public virtual async Task> GetListAsync(GetFilesInput input) + { + return await _privateFileAppService.GetListAsync(input); + } + [HttpGet] [Route("{name}")] [Route("{name}/{process}")] @@ -94,6 +102,20 @@ namespace LINGYUN.Abp.OssManagement ); } + [HttpGet] + [Route("share")] + public virtual async Task> GetShareListAsync() + { + return await _privateFileAppService.GetShareListAsync(); + } + + [HttpPost] + [Route("share")] + public virtual async Task ShareAsync([FromBody] FileShareInput input) + { + return await _privateFileAppService.ShareAsync(input); + } + private static void ThrowValidationException(string message, string memberName) { throw new AbpValidationException(message, diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PublicFilesController.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PublicFilesController.cs index 314676f2b..2675a8ce5 100644 --- a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PublicFilesController.cs +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/PublicFilesController.cs @@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.Threading.Tasks; using Volo.Abp; +using Volo.Abp.Application.Dtos; using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.Http; using Volo.Abp.Validation; @@ -69,6 +70,13 @@ namespace LINGYUN.Abp.OssManagement return await _publicFileAppService.UploadAsync(createOssObjectInput); } + [HttpGet] + [Route("search")] + public virtual async Task> GetListAsync(GetFilesInput input) + { + return await _publicFileAppService.GetListAsync(input); + } + [HttpGet] [Route("{name}")] [Route("{name}/{process}")] diff --git a/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/ShareFilesController.cs b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/ShareFilesController.cs new file mode 100644 index 000000000..a69e8506a --- /dev/null +++ b/aspnet-core/modules/oss-management/LINGYUN.Abp.OssManagement.HttpApi/LINGYUN/Abp/OssManagement/ShareFilesController.cs @@ -0,0 +1,43 @@ +using LINGYUN.Abp.OssManagement.Localization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Volo.Abp; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.Http; + +namespace LINGYUN.Abp.OssManagement +{ + [Area("oss-management")] + [Route("api/files/share")] + [RemoteService(false)] + [ApiExplorerSettings(IgnoreApi = true)] + public class ShareFilesController : AbpController + { + private readonly IShareFileAppService _service; + + public ShareFilesController( + IShareFileAppService service) + { + _service = service; + + LocalizationResource = typeof(AbpOssManagementResource); + } + + [HttpGet] + [Route("{url}")] + public virtual async Task GetAsync(string url) + { + var ossObject = await _service.GetAsync(url); + + if (ossObject.Content.IsNullOrEmpty()) + { + return NotFound(); + } + + return File(ossObject.Content, MimeTypes.GetByExtension(Path.GetExtension(ossObject.Name))); + } + } +}