committed by
GitHub
17 changed files with 680 additions and 84 deletions
@ -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; } |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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; } |
|||
} |
|||
} |
|||
@ -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<FileShareDto> ShareAsync(FileShareInput input); |
|||
|
|||
Task<ListResultDto<MyFileShareDto>> GetShareListAsync(); |
|||
} |
|||
} |
|||
|
|||
@ -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<GetFileShareDto> GetAsync(string url); |
|||
} |
|||
} |
|||
@ -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<FileShareCacheItem> Items { get; set; } |
|||
public MyFileShareCacheItem() { } |
|||
public MyFileShareCacheItem(List<FileShareCacheItem> items) |
|||
{ |
|||
Items = items ?? new List<FileShareCacheItem>(); |
|||
} |
|||
|
|||
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); |
|||
} |
|||
} |
|||
} |
|||
@ -1,17 +1,19 @@ |
|||
using AutoMapper; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement |
|||
{ |
|||
public class OssManagementApplicationAutoMapperProfile : Profile |
|||
{ |
|||
public OssManagementApplicationAutoMapperProfile() |
|||
{ |
|||
CreateMap<OssContainer, OssContainerDto>(); |
|||
CreateMap<OssObject, OssObjectDto>() |
|||
.ForMember(dto => dto.Path, map => map.MapFrom(src => src.Prefix)); |
|||
|
|||
CreateMap<GetOssContainersResponse, OssContainersResultDto>(); |
|||
CreateMap<GetOssObjectsResponse, OssObjectsResultDto>(); |
|||
} |
|||
} |
|||
} |
|||
using AutoMapper; |
|||
|
|||
namespace LINGYUN.Abp.OssManagement |
|||
{ |
|||
public class OssManagementApplicationAutoMapperProfile : Profile |
|||
{ |
|||
public OssManagementApplicationAutoMapperProfile() |
|||
{ |
|||
CreateMap<OssContainer, OssContainerDto>(); |
|||
CreateMap<OssObject, OssObjectDto>() |
|||
.ForMember(dto => dto.Path, map => map.MapFrom(src => src.Prefix)); |
|||
|
|||
CreateMap<GetOssContainersResponse, OssContainersResultDto>(); |
|||
CreateMap<GetOssObjectsResponse, OssObjectsResultDto>(); |
|||
|
|||
CreateMap<FileShareCacheItem, MyFileShareDto>(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -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<FileShareCacheItem> _shareCache; |
|||
private readonly IDistributedCache<MyFileShareCacheItem> _myShareCache; |
|||
private readonly IOssContainerFactory _ossContainerFactory; |
|||
|
|||
public ShareFileAppService( |
|||
IDistributedCache<FileShareCacheItem> shareCache, |
|||
IDistributedCache<MyFileShareCacheItem> myShareCache, |
|||
IOssContainerFactory ossContainerFactory) |
|||
{ |
|||
_shareCache = shareCache; |
|||
_myShareCache = myShareCache; |
|||
_ossContainerFactory = ossContainerFactory; |
|||
} |
|||
|
|||
public virtual async Task<GetFileShareDto> 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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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<AbpOssManagementResource>(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<AbpOssManagementResource>(name); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@ -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<IActionResult> 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))); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue