From 0b6482d75772f8f2aa4cd7b0e6c130dd974c4276 Mon Sep 17 00:00:00 2001
From: root <8907060@qq.com>
Date: Wed, 10 Jan 2024 17:50:43 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E4=BB=B6=E5=AD=98?=
=?UTF-8?q?=E5=82=A8=E6=9C=8D=E5=8A=A1=E7=94=9F=E6=88=90=E4=B8=8B=E8=BD=BD?=
=?UTF-8?q?=E5=9C=B0=E5=9D=80=E5=AE=9E=E7=8E=B0=E6=96=B9=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build/version.props | 2 +-
.../GenerateTempDownloadUrlArgs.cs | 23 +++++++++++
.../IFileStore.cs | 20 +++++++--
.../AliyunFileStore.cs | 41 ++++++++++++++++---
src/Util.FileStorage.Minio/MinioFileStore.cs | 31 +++++++++++---
.../FileNameProcessorFactory.cs | 16 ++------
src/Util.FileStorage/Local/LocalFileStore.cs | 18 ++++++++
.../UserTimeFileNameProcessor.cs | 34 ++++-----------
src/Util.FileStorage/Usings.cs | 1 +
.../Tests/AliyunFileStoreTest.cs | 13 +++---
.../Tests/MinioFileStoreTest.cs | 10 +----
.../Tests/UserTimeFileNameProcessorTest.cs | 8 ++--
12 files changed, 143 insertions(+), 74 deletions(-)
create mode 100644 src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs
diff --git a/build/version.props b/build/version.props
index 931dbe196..66ebed593 100644
--- a/build/version.props
+++ b/build/version.props
@@ -2,7 +2,7 @@
7
1
- 126
+ 127
$(VersionMajor).$(VersionMinor).$(VersionPatch)
diff --git a/src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs b/src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs
new file mode 100644
index 000000000..af8772113
--- /dev/null
+++ b/src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs
@@ -0,0 +1,23 @@
+namespace Util.FileStorage;
+
+///
+/// 生成临时下载Url方法参数
+///
+public class GenerateTempDownloadUrlArgs : FileStorageArgs {
+ ///
+ /// 初始化生成临时下载Url方法参数
+ ///
+ /// 文件名
+ public GenerateTempDownloadUrlArgs( string fileName ) : base( fileName ) {
+ }
+
+ ///
+ /// 下载地址过期时间,单位:秒
+ ///
+ public int? Expiration { get; set; }
+
+ ///
+ /// 响应内容类型
+ ///
+ public string ResponseContentType { get; set; }
+}
\ No newline at end of file
diff --git a/src/Util.FileStorage.Abstractions/IFileStore.cs b/src/Util.FileStorage.Abstractions/IFileStore.cs
index a13f3a95d..0e4c17ec5 100644
--- a/src/Util.FileStorage.Abstractions/IFileStore.cs
+++ b/src/Util.FileStorage.Abstractions/IFileStore.cs
@@ -75,26 +75,38 @@ public interface IFileStore : ILocalFileStore {
/// 取消令牌
Task DeleteFileAsync( DeleteFileArgs args, CancellationToken cancellationToken = default );
///
- /// 生成客户端直接下载Url
+ /// 生成客户端下载Url
///
/// 文件名
/// 取消令牌
Task GenerateDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default );
///
- /// 生成客户端直接下载Url
+ /// 生成客户端下载Url
///
/// 参数
/// 取消令牌
Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default );
///
- /// 生成客户端直接上传Url
+ /// 生成客户端临时下载Url
+ ///
+ /// 文件名
+ /// 取消令牌
+ Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default );
+ ///
+ /// 生成客户端临时下载Url
+ ///
+ /// 参数
+ /// 取消令牌
+ Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default );
+ ///
+ /// 生成客户端直传Url
///
/// 文件名
/// 文件名处理策略
/// 取消令牌
Task GenerateUploadUrlAsync( string fileName, string policy = null, CancellationToken cancellationToken = default );
///
- /// 生成客户端直接上传Url
+ /// 生成客户端直传Url
///
/// 参数
/// 取消令牌
diff --git a/src/Util.FileStorage.Aliyun/AliyunFileStore.cs b/src/Util.FileStorage.Aliyun/AliyunFileStore.cs
index d274abafe..ac48e1d83 100644
--- a/src/Util.FileStorage.Aliyun/AliyunFileStore.cs
+++ b/src/Util.FileStorage.Aliyun/AliyunFileStore.cs
@@ -72,7 +72,7 @@ protected virtual async Task InitConfig() {
///
/// 获取阿里云对象存储客户端
///
- protected virtual async Task GetClient() {
+ public virtual async Task GetClient() {
if( _client != null )
return _client;
await InitConfig();
@@ -408,20 +408,49 @@ protected async Task ProcessBucketName( FileStorageArgs args ) {
}
///
- public async Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
+ public virtual async Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
args.CheckNull( nameof( args ) );
var processedFileName = ProcessFileName( args );
var processedBucketName = await ProcessBucketName( args );
- return await GenerateDownloadUrlAsync( processedFileName, processedBucketName, args.ResponseContentType, cancellationToken );
+ var endpoint = await GetEndPoint( processedBucketName.Name );
+ return Util.Helpers.Common.JoinPath( endpoint, processedFileName.Name );
}
///
- /// 生成下载Url
+ /// 获取端点
///
- protected async Task GenerateDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName, string responseContentType, CancellationToken cancellationToken = default ) {
+ protected virtual async Task GetEndPoint(string bucketName ) {
+ await InitConfig();
+ var endpoint = _config.Endpoint.RemoveStart( "http://" ).RemoveStart( "https://" );
+ return $"https://{bucketName}.{endpoint}";
+ }
+
+ #endregion
+
+ #region GenerateTempDownloadUrlAsync
+
+ ///
+ public async Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) {
+ var args = new GenerateTempDownloadUrlArgs( fileName );
+ return await GenerateTempDownloadUrlAsync( args, cancellationToken );
+ }
+
+ ///
+ public virtual async Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
+ args.CheckNull( nameof( args ) );
+ var processedFileName = ProcessFileName( args );
+ var processedBucketName = await ProcessBucketName( args );
+ var expiration = args.Expiration ?? _config.DownloadUrlExpiration;
+ return await GenerateTempDownloadUrlAsync( processedFileName, processedBucketName, expiration, cancellationToken );
+ }
+
+ ///
+ /// 生成临时下载Url
+ ///
+ protected virtual async Task GenerateTempDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName,int expiration, CancellationToken cancellationToken = default ) {
var client = await GetClient();
var request = new GeneratePresignedUriRequest( bucketName.Name, fileName.Name, SignHttpMethod.Get ) {
- Expiration = DateTime.Now.AddSeconds( _config.UploadUrlExpiration )
+ Expiration = DateTime.Now.AddSeconds( expiration )
};
var result = client.GeneratePresignedUri( request );
return result.AbsoluteUri;
diff --git a/src/Util.FileStorage.Minio/MinioFileStore.cs b/src/Util.FileStorage.Minio/MinioFileStore.cs
index 7c6760d86..ce4299ef0 100644
--- a/src/Util.FileStorage.Minio/MinioFileStore.cs
+++ b/src/Util.FileStorage.Minio/MinioFileStore.cs
@@ -463,24 +463,45 @@ private async Task CreateCopySourceObjectArgs( FileStorage
}
///
- public async Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
+ public virtual async Task GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
args.CheckNull( nameof( args ) );
var processedFileName = ProcessFileName( args );
var processedBucketName = await ProcessBucketName( args );
- return await GenerateDownloadUrlAsync( processedFileName, processedBucketName, args.ResponseContentType,cancellationToken );
+ await InitConfig();
+ var schema = _config.UseSSL ? "https" : "http";
+ return Util.Helpers.Common.JoinPath( $"{schema}://{GetEndpoint()}", processedBucketName.Name, processedFileName.Name );
+ }
+
+ #endregion
+
+ #region GenerateTempDownloadUrlAsync
+
+ ///
+ public async Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) {
+ var args = new GenerateTempDownloadUrlArgs( fileName );
+ return await GenerateTempDownloadUrlAsync( args, cancellationToken );
+ }
+
+ ///
+ public async Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
+ args.CheckNull( nameof( args ) );
+ var processedFileName = ProcessFileName( args );
+ var processedBucketName = await ProcessBucketName( args );
+ var expiration = args.Expiration ?? _config.DownloadUrlExpiration;
+ return await GenerateTempDownloadUrlAsync( processedFileName, processedBucketName, args.ResponseContentType, expiration );
}
///
- /// 生成下载Url
+ /// 生成临时下载Url
///
- protected async Task GenerateDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName, string responseContentType, CancellationToken cancellationToken = default ) {
+ protected async Task GenerateTempDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName, string responseContentType, int expiration ) {
var client = await GetClient();
responseContentType ??= "application/octet-stream";
var headers = new Dictionary { { "response-content-type", responseContentType } };
var args = new PresignedGetObjectArgs()
.WithBucket( bucketName.Name )
.WithObject( fileName.Name )
- .WithExpiry( _config.DownloadUrlExpiration )
+ .WithExpiry( expiration )
.WithHeaders( headers );
return await client.PresignedGetObjectAsync( args );
}
diff --git a/src/Util.FileStorage/FileNameProcessorFactory.cs b/src/Util.FileStorage/FileNameProcessorFactory.cs
index cd9f1816d..c74fdc734 100644
--- a/src/Util.FileStorage/FileNameProcessorFactory.cs
+++ b/src/Util.FileStorage/FileNameProcessorFactory.cs
@@ -1,6 +1,4 @@
-using Util.Sessions;
-
-namespace Util.FileStorage;
+namespace Util.FileStorage;
///
/// 文件名处理器工厂
@@ -10,27 +8,21 @@ public class FileNameProcessorFactory : IFileNameProcessorFactory {
/// 用户会话
///
private readonly ISession _session;
- ///
- /// 文件名过滤器
- ///
- private readonly IFileNameFilter _filter;
///
/// 初始化文件名处理器工厂
///
/// 用户会话
- /// 文件名过滤器
- public FileNameProcessorFactory( ISession session, IFileNameFilter filter ) {
+ public FileNameProcessorFactory( ISession session ) {
_session = session ?? NullSession.Instance;
- _filter = filter;
}
///
public IFileNameProcessor CreateProcessor( string policy ) {
if ( policy.IsEmpty() )
return new FileNameProcessor();
- if ( policy.ToLowerInvariant() == UserTimeFileNameProcessor.Policy )
- return new UserTimeFileNameProcessor( _session, _filter );
+ if ( policy.ToUpperInvariant() == UserTimeFileNameProcessor.Policy )
+ return new UserTimeFileNameProcessor( _session );
throw new NotImplementedException( $"文件名处理策略 {policy} 未实现." );
}
}
\ No newline at end of file
diff --git a/src/Util.FileStorage/Local/LocalFileStore.cs b/src/Util.FileStorage/Local/LocalFileStore.cs
index 894d099ef..26e85f04b 100644
--- a/src/Util.FileStorage/Local/LocalFileStore.cs
+++ b/src/Util.FileStorage/Local/LocalFileStore.cs
@@ -274,6 +274,24 @@ protected virtual async Task GetPhysicalPath( ProcessedName fileName ) {
#endregion
+ #region GenerateTempDownloadUrlAsync
+
+ ///
+ public async Task GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) {
+ var args = new GenerateTempDownloadUrlArgs( fileName );
+ return await GenerateTempDownloadUrlAsync( args, cancellationToken );
+ }
+
+ ///
+ public virtual Task GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
+ args.CheckNull( nameof( args ) );
+ var processedFileName = ProcessFileName( args );
+ var url = Util.Helpers.Common.JoinPath( Util.Helpers.Web.Host, processedFileName.Name );
+ return Task.FromResult( url );
+ }
+
+ #endregion
+
#region GenerateUploadUrlAsync
///
diff --git a/src/Util.FileStorage/UserTimeFileNameProcessor.cs b/src/Util.FileStorage/UserTimeFileNameProcessor.cs
index d88cb7cf8..ab50f1703 100644
--- a/src/Util.FileStorage/UserTimeFileNameProcessor.cs
+++ b/src/Util.FileStorage/UserTimeFileNameProcessor.cs
@@ -1,5 +1,4 @@
using Util.Helpers;
-using Util.Sessions;
namespace Util.FileStorage;
@@ -10,46 +9,27 @@ public class UserTimeFileNameProcessor : IFileNameProcessor {
///
/// 策略名称
///
- public const string Policy = "usertime";
+ public const string Policy = "USERTIME";
///
/// 用户会话
///
private readonly ISession _session;
- ///
- /// 文件名过滤器
- ///
- private readonly IFileNameFilter _filter;
///
/// 初始化基于用户标识和时间的文件名处理器
///
/// 用户会话
- /// 文件名过滤器
- public UserTimeFileNameProcessor( ISession session, IFileNameFilter filter ) {
+ public UserTimeFileNameProcessor( ISession session ) {
_session = session ?? NullSession.Instance;
- _filter = filter;
}
///
public ProcessedName Process( string fileName ) {
- fileName = _filter?.Filter( fileName );
- var result = $"{GetUserId()}{GetTime()}{fileName}";
+ if ( fileName.IsEmpty() )
+ return new ProcessedName( null );
+ var extension = Path.GetExtension( fileName );
+ var name = $"{Id.Create()}{extension}";
+ var result = Util.Helpers.Common.JoinPath( _session.UserId, $"{Time.Now:yyyy-MM-dd}", name );
return new ProcessedName( result, fileName );
}
-
- ///
- /// 获取用户标识
- ///
- private string GetUserId() {
- if ( _session.UserId.IsEmpty() )
- return null;
- return $"{_session.UserId}/";
- }
-
- ///
- /// 获取时间
- ///
- private string GetTime() {
- return $"{Time.Now:yyyy-MM-dd-HH-mm-ss-fff}/";
- }
}
\ No newline at end of file
diff --git a/src/Util.FileStorage/Usings.cs b/src/Util.FileStorage/Usings.cs
index d87a5e5d0..0e6b7d400 100644
--- a/src/Util.FileStorage/Usings.cs
+++ b/src/Util.FileStorage/Usings.cs
@@ -12,3 +12,4 @@
global using FileSignatures.Formats;
global using Util.Dependency;
global using Util.Http;
+global using Util.Sessions;
diff --git a/test/Util.FileStorage.Aliyun.Tests.Integration/Tests/AliyunFileStoreTest.cs b/test/Util.FileStorage.Aliyun.Tests.Integration/Tests/AliyunFileStoreTest.cs
index 510fd8cca..969ac3860 100644
--- a/test/Util.FileStorage.Aliyun.Tests.Integration/Tests/AliyunFileStoreTest.cs
+++ b/test/Util.FileStorage.Aliyun.Tests.Integration/Tests/AliyunFileStoreTest.cs
@@ -344,15 +344,12 @@ public async Task TestDeleteFileAsync() {
///
[Fact]
public async Task TestGenerateDownloadUrlAsync() {
- //保存文件
- var path = Common.GetPhysicalPath( "~/Resources/a.png" );
- var fileInfo = new FileInfo( path );
- var result = await _fileStore.SaveFileAsync( fileInfo );
-
- //生成url
- var url = await _fileStore.GenerateDownloadUrlAsync( result.FileName );
+ var args = new GenerateDownloadUrlArgs("a.jpg") {
+ BucketName = "test"
+ };
+ var url = await _fileStore.GenerateDownloadUrlAsync( args );
_testOutputHelper.WriteLine( url );
- Assert.StartsWith( "http", url );
+ Assert.StartsWith( "https://test.oss-cn-beijing.aliyuncs.com/a.jpg", url );
}
#endregion
diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Tests/MinioFileStoreTest.cs b/test/Util.FileStorage.Minio.Tests.Integration/Tests/MinioFileStoreTest.cs
index 7a6c5f5b5..69b78d1bb 100644
--- a/test/Util.FileStorage.Minio.Tests.Integration/Tests/MinioFileStoreTest.cs
+++ b/test/Util.FileStorage.Minio.Tests.Integration/Tests/MinioFileStoreTest.cs
@@ -387,15 +387,9 @@ public async Task TestDeleteFileAsync() {
///
[Fact]
public async Task TestGenerateDownloadUrlAsync() {
- //保存文件
- var path = Common.GetPhysicalPath( "~/Resources/b.jpg" );
- var fileInfo = new FileInfo( path );
- var result = await _fileStore.SaveFileAsync( fileInfo );
-
- //生成url
- var url = await _fileStore.GenerateDownloadUrlAsync( "a" );
+ var url = await _fileStore.GenerateDownloadUrlAsync( "a.jpg" );
_testOutputHelper.WriteLine( url );
- Assert.StartsWith( "http", url );
+ Assert.Equal( "http://127.0.0.1:9000/util-filestorage-minio-test/a.jpg", url );
}
#endregion
diff --git a/test/Util.FileStorage.Minio.Tests.Integration/Tests/UserTimeFileNameProcessorTest.cs b/test/Util.FileStorage.Minio.Tests.Integration/Tests/UserTimeFileNameProcessorTest.cs
index 5da7dfeec..e32f016e3 100644
--- a/test/Util.FileStorage.Minio.Tests.Integration/Tests/UserTimeFileNameProcessorTest.cs
+++ b/test/Util.FileStorage.Minio.Tests.Integration/Tests/UserTimeFileNameProcessorTest.cs
@@ -13,7 +13,8 @@ public class UserTimeFileNameProcessorTest : IDisposable {
/// 测试初始化
///
public UserTimeFileNameProcessorTest() {
- Time.SetTime( new DateTime( 2012, 12, 12, 12, 12, 12, 123 ) );
+ Time.SetTime( new DateTime( 2012, 12, 12 ) );
+ Id.SetId( "id" );
}
///
@@ -21,6 +22,7 @@ public UserTimeFileNameProcessorTest() {
///
public void Dispose() {
Time.Reset();
+ Id.Reset();
}
///
@@ -28,8 +30,8 @@ public void Dispose() {
///
[Fact]
public void TestProcess_1() {
- var processor = new UserTimeFileNameProcessor( new TestSession(),new FileNameFilter() );
+ var processor = new UserTimeFileNameProcessor( new TestSession() );
var result = processor.Process( "a.jpg" );
- Assert.Equal( $"{TestSession.TestUserId}/2012-12-12-12-12-12-123/a.jpg", result.Name );
+ Assert.Equal( $"{TestSession.TestUserId}/2012-12-12/id.jpg", result.Name );
}
}
\ No newline at end of file