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 |
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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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