Skip to content

Commit

Permalink
修改文件存储服务生成下载地址实现方式
Browse files Browse the repository at this point in the history
  • Loading branch information
UtilCore committed Jan 10, 2024
1 parent 944407a commit 0b6482d
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 74 deletions.
2 changes: 1 addition & 1 deletion build/version.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<VersionMajor>7</VersionMajor>
<VersionMinor>1</VersionMinor>
<VersionPatch>126</VersionPatch>
<VersionPatch>127</VersionPatch>
<VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>
<VersionSuffix></VersionSuffix>
</PropertyGroup>
Expand Down
23 changes: 23 additions & 0 deletions src/Util.FileStorage.Abstractions/GenerateTempDownloadUrlArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Util.FileStorage;

/// <summary>
/// 生成临时下载Url方法参数
/// </summary>
public class GenerateTempDownloadUrlArgs : FileStorageArgs {
/// <summary>
/// 初始化生成临时下载Url方法参数
/// </summary>
/// <param name="fileName">文件名</param>
public GenerateTempDownloadUrlArgs( string fileName ) : base( fileName ) {
}

/// <summary>
/// 下载地址过期时间,单位:秒
/// </summary>
public int? Expiration { get; set; }

/// <summary>
/// 响应内容类型
/// </summary>
public string ResponseContentType { get; set; }
}
20 changes: 16 additions & 4 deletions src/Util.FileStorage.Abstractions/IFileStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,38 @@ public interface IFileStore : ILocalFileStore {
/// <param name="cancellationToken">取消令牌</param>
Task DeleteFileAsync( DeleteFileArgs args, CancellationToken cancellationToken = default );
/// <summary>
/// 生成客户端直接下载Url
/// 生成客户端下载Url
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="cancellationToken">取消令牌</param>
Task<string> GenerateDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default );
/// <summary>
/// 生成客户端直接下载Url
/// 生成客户端下载Url
/// </summary>
/// <param name="args">参数</param>
/// <param name="cancellationToken">取消令牌</param>
Task<string> GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default );
/// <summary>
/// 生成客户端直接上传Url
/// 生成客户端临时下载Url
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="cancellationToken">取消令牌</param>
Task<string> GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default );
/// <summary>
/// 生成客户端临时下载Url
/// </summary>
/// <param name="args">参数</param>
/// <param name="cancellationToken">取消令牌</param>
Task<string> GenerateTempDownloadUrlAsync( GenerateTempDownloadUrlArgs args, CancellationToken cancellationToken = default );
/// <summary>
/// 生成客户端直传Url
/// </summary>
/// <param name="fileName">文件名</param>
/// <param name="policy">文件名处理策略</param>
/// <param name="cancellationToken">取消令牌</param>
Task<DirectUploadParam> GenerateUploadUrlAsync( string fileName, string policy = null, CancellationToken cancellationToken = default );
/// <summary>
/// 生成客户端直接上传Url
/// 生成客户端直传Url
/// </summary>
/// <param name="args">参数</param>
/// <param name="cancellationToken">取消令牌</param>
Expand Down
41 changes: 35 additions & 6 deletions src/Util.FileStorage.Aliyun/AliyunFileStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ protected virtual async Task InitConfig() {
/// <summary>
/// 获取阿里云对象存储客户端
/// </summary>
protected virtual async Task<IOss> GetClient() {
public virtual async Task<IOss> GetClient() {
if( _client != null )
return _client;
await InitConfig();
Expand Down Expand Up @@ -408,20 +408,49 @@ protected async Task<ProcessedName> ProcessBucketName( FileStorageArgs args ) {
}

/// <inheritdoc />
public async Task<string> GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
public virtual async Task<string> 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 );
}

/// <summary>
/// 生成下载Url
/// 获取端点
/// </summary>
protected async Task<string> GenerateDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName, string responseContentType, CancellationToken cancellationToken = default ) {
protected virtual async Task<string> GetEndPoint(string bucketName ) {
await InitConfig();
var endpoint = _config.Endpoint.RemoveStart( "http://" ).RemoveStart( "https://" );
return $"https://{bucketName}.{endpoint}";
}

#endregion

#region GenerateTempDownloadUrlAsync

/// <inheritdoc />
public async Task<string> GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) {
var args = new GenerateTempDownloadUrlArgs( fileName );
return await GenerateTempDownloadUrlAsync( args, cancellationToken );
}

/// <inheritdoc />
public virtual async Task<string> 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 );
}

/// <summary>
/// 生成临时下载Url
/// </summary>
protected virtual async Task<string> 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;
Expand Down
31 changes: 26 additions & 5 deletions src/Util.FileStorage.Minio/MinioFileStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -463,24 +463,45 @@ private async Task<CopySourceObjectArgs> CreateCopySourceObjectArgs( FileStorage
}

/// <inheritdoc />
public async Task<string> GenerateDownloadUrlAsync( GenerateDownloadUrlArgs args, CancellationToken cancellationToken = default ) {
public virtual async Task<string> 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

/// <inheritdoc />
public async Task<string> GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) {
var args = new GenerateTempDownloadUrlArgs( fileName );
return await GenerateTempDownloadUrlAsync( args, cancellationToken );
}

/// <inheritdoc />
public async Task<string> 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 );
}

/// <summary>
/// 生成下载Url
/// 生成临时下载Url
/// </summary>
protected async Task<string> GenerateDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName, string responseContentType, CancellationToken cancellationToken = default ) {
protected async Task<string> GenerateTempDownloadUrlAsync( ProcessedName fileName, ProcessedName bucketName, string responseContentType, int expiration ) {
var client = await GetClient();
responseContentType ??= "application/octet-stream";
var headers = new Dictionary<string, string> { { "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 );
}
Expand Down
16 changes: 4 additions & 12 deletions src/Util.FileStorage/FileNameProcessorFactory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using Util.Sessions;

namespace Util.FileStorage;
namespace Util.FileStorage;

/// <summary>
/// 文件名处理器工厂
Expand All @@ -10,27 +8,21 @@ public class FileNameProcessorFactory : IFileNameProcessorFactory {
/// 用户会话
/// </summary>
private readonly ISession _session;
/// <summary>
/// 文件名过滤器
/// </summary>
private readonly IFileNameFilter _filter;

/// <summary>
/// 初始化文件名处理器工厂
/// </summary>
/// <param name="session">用户会话</param>
/// <param name="filter">文件名过滤器</param>
public FileNameProcessorFactory( ISession session, IFileNameFilter filter ) {
public FileNameProcessorFactory( ISession session ) {
_session = session ?? NullSession.Instance;
_filter = filter;
}

/// <inheritdoc />
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} 未实现." );
}
}
18 changes: 18 additions & 0 deletions src/Util.FileStorage/Local/LocalFileStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,24 @@ protected virtual async Task<string> GetPhysicalPath( ProcessedName fileName ) {

#endregion

#region GenerateTempDownloadUrlAsync

/// <inheritdoc />
public async Task<string> GenerateTempDownloadUrlAsync( string fileName, CancellationToken cancellationToken = default ) {
var args = new GenerateTempDownloadUrlArgs( fileName );
return await GenerateTempDownloadUrlAsync( args, cancellationToken );
}

/// <inheritdoc />
public virtual Task<string> 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

/// <inheritdoc />
Expand Down
34 changes: 7 additions & 27 deletions src/Util.FileStorage/UserTimeFileNameProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Util.Helpers;
using Util.Sessions;

namespace Util.FileStorage;

Expand All @@ -10,46 +9,27 @@ public class UserTimeFileNameProcessor : IFileNameProcessor {
/// <summary>
/// 策略名称
/// </summary>
public const string Policy = "usertime";
public const string Policy = "USERTIME";
/// <summary>
/// 用户会话
/// </summary>
private readonly ISession _session;
/// <summary>
/// 文件名过滤器
/// </summary>
private readonly IFileNameFilter _filter;

/// <summary>
/// 初始化基于用户标识和时间的文件名处理器
/// </summary>
/// <param name="session">用户会话</param>
/// <param name="filter">文件名过滤器</param>
public UserTimeFileNameProcessor( ISession session, IFileNameFilter filter ) {
public UserTimeFileNameProcessor( ISession session ) {
_session = session ?? NullSession.Instance;
_filter = filter;
}

/// <inheritdoc />
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 );
}

/// <summary>
/// 获取用户标识
/// </summary>
private string GetUserId() {
if ( _session.UserId.IsEmpty() )
return null;
return $"{_session.UserId}/";
}

/// <summary>
/// 获取时间
/// </summary>
private string GetTime() {
return $"{Time.Now:yyyy-MM-dd-HH-mm-ss-fff}/";
}
}
1 change: 1 addition & 0 deletions src/Util.FileStorage/Usings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
global using FileSignatures.Formats;
global using Util.Dependency;
global using Util.Http;
global using Util.Sessions;
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,12 @@ public async Task TestDeleteFileAsync() {
/// </summary>
[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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,15 +387,9 @@ public async Task TestDeleteFileAsync() {
/// </summary>
[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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,25 @@ public class UserTimeFileNameProcessorTest : IDisposable {
/// 测试初始化
/// </summary>
public UserTimeFileNameProcessorTest() {
Time.SetTime( new DateTime( 2012, 12, 12, 12, 12, 12, 123 ) );
Time.SetTime( new DateTime( 2012, 12, 12 ) );
Id.SetId( "id" );
}

/// <summary>
/// 测试清理
/// </summary>
public void Dispose() {
Time.Reset();
Id.Reset();
}

/// <summary>
/// 测试处理文件名
/// </summary>
[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 );
}
}

0 comments on commit 0b6482d

Please sign in to comment.