Browse Source

feat: Optimize BlobProvider

- 阿里云OSS SDK v4签名存在缺陷, 默认使用v1签名
- 阿里云OSS SDK v2不支持CORS配置, 默认不开启预览, 使用后台代理链接预览文件
- 阿里云BlobProvider增加多个适配SDK v2的配置项
- 腾讯云OSS默认不支持文件预览功能, 需要额外开通数据处理服务, 默认不开启预览, 使用后台代理链接预览文件
pull/1490/head
colin 2 days ago
parent
commit
b4960fa2e4
  1. 11
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/AliyunClientFactory.cs
  2. 8
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs
  3. 45
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs
  4. 67
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs
  5. 12
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs
  6. 47
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/OssClientFactory.cs
  7. 136
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Aliyun/LINGYUN/Abp/BlobManagement/Aliyun/AliyunBlobProvider.cs
  8. 12
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/BlobAppServiceBase.cs
  9. 88
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Tencent/LINGYUN/Abp/BlobManagement/Tencent/TencentBlobProvider.cs

11
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/AliyunClientFactory.cs

@ -98,20 +98,19 @@ public abstract class AliyunClientFactory<TClient>
assumeRoleResponse.Body.Credentials.AccessKeySecret,
assumeRoleResponse.Body.Credentials.SecurityToken);
var expirationTimeSpan = TimeSpan.FromSeconds(durationSeconds - 10);
var cacheOptions = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(durationSeconds - 10),
};
if (DateTime.TryParse(assumeRoleResponse.Body.Credentials.Expiration, out var expiration))
{
cacheItem.Expiration = expiration;
expirationTimeSpan = new TimeSpan(expiration.AddSeconds(-10).Ticks);
}
await Cache.SetAsync(
cacheKey,
cacheItem,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = expirationTimeSpan,
});
cacheOptions);
}
return cacheItem;

8
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs

@ -1,4 +1,5 @@
using System.Text.RegularExpressions;
using System;
using System.Text.RegularExpressions;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
@ -14,7 +15,10 @@ public class AliyunBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDepen
/// <returns></returns>
public virtual string NormalizeBlobName(string blobName)
{
return blobName;
// 不能以正斜线(/)或者反斜线(\)开头。
return blobName
.RemovePreFix("/")
.RemovePreFix("\\");
}
/// <summary>

45
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs

@ -35,7 +35,7 @@ public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
public override async Task<bool> DeleteAsync(BlobProviderDeleteArgs args)
{
var ossClient = await GetOssClientAsync(args);
using var ossClient = await GetOssClientAsync(args);
var blobName = AliyunBlobNameCalculator.Calculate(args);
if (await BlobExistsAsync(ossClient, args, blobName))
@ -55,7 +55,7 @@ public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
public override async Task<bool> ExistsAsync(BlobProviderExistsArgs args)
{
var ossClient = await GetOssClientAsync(args);
using var ossClient = await GetOssClientAsync(args);
var blobName = AliyunBlobNameCalculator.Calculate(args);
return await BlobExistsAsync(ossClient, args, blobName);
@ -63,40 +63,39 @@ public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
public override async Task<Stream> GetOrNullAsync(BlobProviderGetArgs args)
{
var ossClient = await GetOssClientAsync(args);
using var ossClient = await GetOssClientAsync(args);
var blobName = AliyunBlobNameCalculator.Calculate(args);
if (!await BlobExistsAsync(ossClient, args, blobName))
{
return null;
}
var result = await ossClient.GetObjectAsync(
new GetObjectRequest
{
Bucket = GetBucketName(args),
Key = blobName,
});
return result.Body;
//if (!await BlobExistsAsync(ossClient, args, blobName))
//{
// return null;
//}
// TODO: 阿里云sdk预签名不可用[2026/05/23]
//var configuration = args.Configuration.GetAliyunConfiguration();
//var presignResult = ossClient.Presign(
//var result = await ossClient.GetObjectAsync(
// new GetObjectRequest
// {
// Bucket = GetBucketName(args),
// Key = blobName,
// },
// Clock.Now.AddSeconds(configuration.PresignedGetExpirySeconds));
// });
//return result.Body;
var configuration = args.Configuration.GetAliyunConfiguration();
var presignResult = ossClient.Presign(
new GetObjectRequest
{
Bucket = GetBucketName(args),
Key = blobName,
},
Clock.Now.AddSeconds(configuration.PresignedGetExpirySeconds));
//var httpClient = HttpClientFactory.CreateAliyunHttpClient();
var httpClient = HttpClientFactory.CreateAliyunHttpClient();
//return await httpClient.GetStreamAsync(presignResult.Url, args.CancellationToken);
return await httpClient.GetStreamAsync(presignResult.Url, args.CancellationToken);
}
public override async Task SaveAsync(BlobProviderSaveArgs args)
{
var ossClient = await GetOssClientAsync(args);
using var ossClient = await GetOssClientAsync(args);
var blobName = AliyunBlobNameCalculator.Calculate(args);
var configuration = args.Configuration.GetAliyunConfiguration();

67
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs

@ -6,6 +6,22 @@ namespace LINGYUN.Abp.BlobStoring.Aliyun;
public class AliyunBlobProviderConfiguration
{
/// <summary>
/// 默认签名版本
/// </summary>
public const string DefaultSignatureVersion = "v1"; //TODO: 阿里云 OSS SDKv2 SignerV4.ResourcePath存在缺陷, 临时使用v1签名
/// <summary>
/// 默认跳过服务器证书验证
/// </summary>
public const bool DefaultInsecureSkipVerify = false;
/// <summary>
/// 默认命名空间不存在是否创建
/// </summary>
public const bool DefaultCreateBucketIfNotExists = false;
/// <summary>
/// 默认预签名链接过期时间
/// </summary>
public const int DefaultPresignedGetExpirySeconds = 600;
/// <summary>
/// 命名空间
/// </summary>
@ -15,10 +31,18 @@ public class AliyunBlobProviderConfiguration
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.BucketName, value);
}
/// <summary>
/// 签名版本(可选项:v1、v4)
/// 默认: v1
/// </summary>
public string SignatureVersion {
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.SignatureVersion, DefaultSignatureVersion);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.SignatureVersion, value);
}
/// <summary>
/// 跳过服务器证书验证
/// </summary>
public bool InsecureSkipVerify {
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.InsecureSkipVerify, false);
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.InsecureSkipVerify, DefaultInsecureSkipVerify);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.InsecureSkipVerify, value);
}
/// <summary>
@ -26,7 +50,7 @@ public class AliyunBlobProviderConfiguration
/// </summary>
public bool CreateBucketIfNotExists
{
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists, false);
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists, DefaultCreateBucketIfNotExists);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.CreateBucketIfNotExists, value);
}
/// <summary>
@ -55,15 +79,44 @@ public class AliyunBlobProviderConfiguration
}
}
/// <summary>
/// Default value: 7 * 24 * 3600.
/// </summary>
public int PresignedGetExpirySeconds {
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.PresignedGetExpirySeconds, _defaultExpirySeconds);
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.PresignedGetExpirySeconds, DefaultPresignedGetExpirySeconds);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.PresignedGetExpirySeconds, value);
}
private int _defaultExpirySeconds = 7 * 24 * 3600;
public string Endpoint {
get => _containerConfiguration.GetConfigurationOrDefault<string>(AliyunBlobProviderConfigurationNames.Endpoint, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint, value);
}
public bool? UsePathStyle {
get => _containerConfiguration.GetConfigurationOrDefault<bool?>(AliyunBlobProviderConfigurationNames.UsePathStyle, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.UsePathStyle, value);
}
public bool? UseCName {
get => _containerConfiguration.GetConfigurationOrDefault<bool?>(AliyunBlobProviderConfigurationNames.UseCName, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.UseCName, value);
}
public bool? UseDualStackEndpoint {
get => _containerConfiguration.GetConfigurationOrDefault<bool?>(AliyunBlobProviderConfigurationNames.UseDualStackEndpoint, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.UseDualStackEndpoint, value);
}
public bool? UseAccelerateEndpoint {
get => _containerConfiguration.GetConfigurationOrDefault<bool?>(AliyunBlobProviderConfigurationNames.UseAccelerateEndpoint, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.UseAccelerateEndpoint, value);
}
public bool? UseInternalEndpoint {
get => _containerConfiguration.GetConfigurationOrDefault<bool?>(AliyunBlobProviderConfigurationNames.UseInternalEndpoint, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.UseInternalEndpoint, value);
}
public bool? DisableUploadCrc64Check {
get => _containerConfiguration.GetConfigurationOrDefault<bool?>(AliyunBlobProviderConfigurationNames.DisableUploadCrc64Check, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.DisableUploadCrc64Check, value);
}
public bool? DisableDownloadCrc64Check {
get => _containerConfiguration.GetConfigurationOrDefault<bool?>(AliyunBlobProviderConfigurationNames.DisableDownloadCrc64Check, null);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.DisableDownloadCrc64Check, value);
}
private readonly BlobContainerConfiguration _containerConfiguration;

12
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs

@ -11,6 +11,10 @@ public static class AliyunBlobProviderConfigurationNames
/// </summary>
public const string BucketName = "Aliyun:OSS:BucketName";
/// <summary>
/// 签名版本(可选项:v1、v4)
/// </summary>
public const string SignatureVersion = "Aliyun:OSS:SignatureVersion";
/// <summary>
/// 跳过服务器证书验证
/// </summary>
public const string InsecureSkipVerify = "Aliyun:OSS:InsecureSkipVerify";
@ -30,4 +34,12 @@ public static class AliyunBlobProviderConfigurationNames
/// 生成预签名Uri的过期时间(s)
/// </summary>
public const string PresignedGetExpirySeconds = "Aliyun:OSS:PresignedGetExpirySeconds";
public const string UsePathStyle = "Aliyun:OSS:UsePathStyle";
public const string UseCName = "Aliyun:OSS:UseCName";
public const string UseDualStackEndpoint = "Aliyun:OSS:UseDualStackEndpoint";
public const string UseAccelerateEndpoint = "Aliyun:OSS:UseAccelerateEndpoint";
public const string UseInternalEndpoint = "Aliyun:OSS:UseInternalEndpoint";
public const string DisableUploadCrc64Check = "Aliyun:OSS:DisableUploadCrc64Check";
public const string DisableDownloadCrc64Check = "Aliyun:OSS:DisableDownloadCrc64Check";
}

47
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/OssClientFactory.cs

@ -1,5 +1,6 @@
using AlibabaCloud.OSS.V2;
using AlibabaCloud.OSS.V2.Credentials;
using AlibabaCloud.OSS.V2.Transport;
using LINGYUN.Abp.Aliyun;
using System;
using Volo.Abp.Caching;
@ -31,10 +32,10 @@ public class OssClientFactory : AliyunClientFactory<Client, AliyunBlobProviderCo
new Configuration
{
Region = regionId,
CredentialsProvider = new CredentialsProviderFunc(() =>
{
return new Credentials(accessKeyId, accessKeySecret);
}),
SignatureVersion = AliyunBlobProviderConfiguration.DefaultSignatureVersion,
InsecureSkipVerify = AliyunBlobProviderConfiguration.DefaultInsecureSkipVerify,
CredentialsProvider = new StaticCredentialsProvider(accessKeyId, accessKeySecret),
HttpTransport = HttpTransport.Shared,
});
//return new OssClient(
// regionId,
@ -56,11 +57,18 @@ public class OssClientFactory : AliyunClientFactory<Client, AliyunBlobProviderCo
new Configuration
{
Region = regionId,
CredentialsProvider = new CredentialsProviderFunc(() =>
{
return new Credentials(accessKeyId, accessKeySecret);
}),
Endpoint = configuration.Endpoint,
SignatureVersion = configuration.SignatureVersion,
InsecureSkipVerify = configuration.InsecureSkipVerify,
UseCName = configuration.UseCName,
UsePathStyle = configuration.UsePathStyle,
UseAccelerateEndpoint = configuration.UseAccelerateEndpoint,
UseDualStackEndpoint = configuration.UseDualStackEndpoint,
UseInternalEndpoint = configuration.UseInternalEndpoint,
DisableUploadCrc64Check = configuration.DisableUploadCrc64Check,
DisableDownloadCrc64Check = configuration.DisableDownloadCrc64Check,
CredentialsProvider = new StaticCredentialsProvider(accessKeyId, accessKeySecret),
HttpTransport = HttpTransport.Shared,
});
}
@ -83,10 +91,10 @@ public class OssClientFactory : AliyunClientFactory<Client, AliyunBlobProviderCo
new Configuration
{
Region = regionId,
CredentialsProvider = new CredentialsProviderFunc(() =>
{
return new Credentials(accessKeyId, accessKeySecret, securityToken, expiration);
}),
SignatureVersion = AliyunBlobProviderConfiguration.DefaultSignatureVersion,
InsecureSkipVerify = AliyunBlobProviderConfiguration.DefaultInsecureSkipVerify,
CredentialsProvider = new StaticCredentialsProvider(accessKeyId, accessKeySecret, securityToken),
HttpTransport = HttpTransport.Shared,
});
}
/// <summary>
@ -110,11 +118,18 @@ public class OssClientFactory : AliyunClientFactory<Client, AliyunBlobProviderCo
new Configuration
{
Region = regionId,
CredentialsProvider = new CredentialsProviderFunc(() =>
{
return new Credentials(accessKeyId, accessKeySecret, securityToken, expiration);
}),
Endpoint = configuration.Endpoint,
SignatureVersion = configuration.SignatureVersion,
InsecureSkipVerify = configuration.InsecureSkipVerify,
UseCName = configuration.UseCName,
UsePathStyle = configuration.UsePathStyle,
UseAccelerateEndpoint = configuration.UseAccelerateEndpoint,
UseDualStackEndpoint = configuration.UseDualStackEndpoint,
UseInternalEndpoint = configuration.UseInternalEndpoint,
DisableUploadCrc64Check = configuration.DisableUploadCrc64Check,
DisableDownloadCrc64Check = configuration.DisableDownloadCrc64Check,
CredentialsProvider = new StaticCredentialsProvider(accessKeyId, accessKeySecret, securityToken),
HttpTransport = HttpTransport.Shared,
});
}
}

136
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Aliyun/LINGYUN/Abp/BlobManagement/Aliyun/AliyunBlobProvider.cs

@ -49,7 +49,7 @@ public class AliyunBlobProvider : IBlobProvider
string name,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
using var client = await CreateClientAsync();
var bucket = NormalizeContainerName(name);
var configuration = GetBlobConfiguration();
@ -60,7 +60,7 @@ public class AliyunBlobProvider : IBlobProvider
string name,
CancellationToken cancellationToken = default)
{
var ossClient = await CreateClientAsync();
using var ossClient = await CreateClientAsync();
var bucket = NormalizeContainerName(name);
if (!await BucketExists(ossClient, bucket, cancellationToken))
@ -81,7 +81,7 @@ public class AliyunBlobProvider : IBlobProvider
string blobName,
CancellationToken cancellationToken = default)
{
var ossClient = await CreateClientAsync();
using var ossClient = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
var objectName = CalculateBlobName(blobName);
@ -111,40 +111,39 @@ public class AliyunBlobProvider : IBlobProvider
string blobName,
CancellationToken cancellationToken = default)
{
var ossClient = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
var objectName = CalculateBlobName(blobName);
//using var ossClient = await CreateClientAsync();
//var bucket = NormalizeContainerName(containerName);
//var objectName = CalculateBlobName(blobName);
if (!await ObjectExists(ossClient, bucket, objectName, cancellationToken))
{
return null;
}
//if (!await ObjectExists(ossClient, bucket, objectName, cancellationToken))
//{
// return null;
//}
var result = await ossClient.GetObjectAsync(
new GetObjectRequest
{
Bucket = bucket,
Key = blobName,
});
//var result = await ossClient.GetObjectAsync(
// new GetObjectRequest
// {
// Bucket = bucket,
// Key = objectName,
// });
return result.Body;
//return result.Body;
// TODO: 阿里云sdk预签名不可用[2026/05/23]
//var configuration = GetBlobConfiguration();
var configuration = GetBlobConfiguration();
//var downloadUrl = await GeneratePresignedUrlAsync(
// containerName,
// blobName,
// TimeSpan.FromSeconds(configuration.PresignedGetExpirySeconds),
// cancellationToken: cancellationToken);
//if (downloadUrl.IsNullOrWhiteSpace())
//{
// return null;
//}
var downloadUrl = await InternalGeneratePresignedUrlAsync(
containerName,
blobName,
TimeSpan.FromSeconds(configuration.PresignedGetExpirySeconds),
cancellationToken: cancellationToken);
if (downloadUrl.IsNullOrWhiteSpace())
{
return null;
}
//var httpClient = HttpClientFactory.CreateAliyunHttpClient();
var httpClient = HttpClientFactory.CreateAliyunHttpClient();
//return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
}
public virtual Task<string?> GeneratePresignedUrlAsync(
@ -154,32 +153,14 @@ public class AliyunBlobProvider : IBlobProvider
bool isAttachmentContent = true,
CancellationToken cancellationToken = default)
{
// TODO: 阿里云sdk预签名不可用[2026/05/23]
// TODO: 阿里云SDK2.0不支持Bucket跨域配置, 不启用阿里云的预览方式
//return InternalGeneratePresignedUrlAsync(
// containerName,
// blobName,
// expiration,
// isAttachmentContent,
// cancellationToken);
return Task.FromResult<string?>(null);
//var ossClient = await CreateClientAsync();
//var bucket = NormalizeContainerName(containerName);
//var objectName = CalculateBlobName(blobName);
//if (!await ObjectExists(ossClient, bucket, objectName, cancellationToken))
//{
// return null;
//}
//var fileName = Path.GetFileName(blobName);
//var type = isAttachmentContent ? "attachment" : "inline";
//var disposition = $"{type}; filename=\"{Uri.EscapeDataString(fileName)}\"; " +
// $"filename*=UTF-8''{Uri.EscapeDataString(fileName)}";
//var presignResult = ossClient.Presign(
// new GetObjectRequest
// {
// Bucket = bucket,
// Key = blobName,
// ResponseContentDisposition = disposition,
// },
// Clock.Now.Add(expiration));
//return presignResult.Url;
}
public virtual Task CreateFolderAsync(
@ -199,7 +180,7 @@ public class AliyunBlobProvider : IBlobProvider
string? contentType = null,
CancellationToken cancellationToken = default)
{
var ossClient = await CreateClientAsync();
using var ossClient = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
var objectName = CalculateBlobName(blobName);
var configuration = GetBlobConfiguration();
@ -241,20 +222,43 @@ public class AliyunBlobProvider : IBlobProvider
new PutBucketRequest
{
Bucket = bucket,
Acl = bucketAcl?.GetString(),
},
cancellationToken: cancellationToken);
}
}
if (bucketAcl.HasValue)
{
await ossClient.PutBucketAclAsync(
new PutBucketAclRequest
{
Bucket = bucket,
Acl = bucketAcl.Value.GetString(),
},
cancellationToken: cancellationToken);
}
protected async virtual Task<string?> InternalGeneratePresignedUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
bool isAttachmentContent = true,
CancellationToken cancellationToken = default)
{
using var ossClient = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
var objectName = CalculateBlobName(blobName);
if (!await ObjectExists(ossClient, bucket, objectName, cancellationToken))
{
return null;
}
var fileName = Path.GetFileName(blobName);
var type = isAttachmentContent ? "attachment" : "inline";
var disposition = $"{type}; filename=\"{Uri.EscapeDataString(fileName)}\"; " +
$"filename*=UTF-8''{Uri.EscapeDataString(fileName)}";
var presignResult = ossClient.Presign(
new GetObjectRequest
{
Bucket = bucket,
Key = objectName,
ResponseContentDisposition = disposition,
},
Clock.Now.Add(expiration));
return presignResult.Url;
}
protected async virtual Task<bool> BucketExists(

12
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/BlobAppServiceBase.cs

@ -64,7 +64,11 @@ public abstract class BlobAppServiceBase : BlobManagementApplicationService
var stream = await BlobManager.DownloadBlobsync(blob);
return new RemoteStreamContent(stream ?? Stream.Null, blob.Name, blob.ContentType, stream?.Length);
return new RemoteStreamContent(
stream ?? Stream.Null,
blob.Name,
blob.ContentType,
stream != null ? blob.Size : null);
}
public async virtual Task<BlobDto> GetAsync(Guid id)
@ -169,7 +173,11 @@ public abstract class BlobAppServiceBase : BlobManagementApplicationService
var stream = await BlobManager.DownloadBlobsync(blob);
return new RemoteStreamContent(stream ?? Stream.Null, blob.Name, blob.ContentType, stream?.Length);
return new RemoteStreamContent(
stream ?? Stream.Null,
blob.Name,
blob.ContentType,
stream != null ? blob.Size : null);
}
protected async virtual Task<string> GenerateDownloadUrlAsync(Guid id, string method, bool isAttachmentContent = true)

88
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Tencent/LINGYUN/Abp/BlobManagement/Tencent/TencentBlobProvider.cs

@ -104,7 +104,7 @@ public class TencentBlobProvider : IBlobProvider
{
var configuration = await GetBlobConfiguration();
var downloadUrl = await GeneratePresignedUrlAsync(
var downloadUrl = await InternalGeneratePresignedUrlAsync(
containerName,
blobName,
TimeSpan.FromSeconds(configuration.PresignedGetExpirySeconds),
@ -119,11 +119,61 @@ public class TencentBlobProvider : IBlobProvider
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
}
public async virtual Task<string?> GeneratePresignedUrlAsync(
public virtual Task<string?> GeneratePresignedUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
bool isAttachmentContent = true,
bool isAttachmentContent = true,
CancellationToken cancellationToken = default)
{
// TODO: 腾讯云需用户开通对象处理服务以支持预览, 不启用腾讯云的预览方式.
//return InternalGeneratePresignedUrlAsync(
// containerName,
// blobName,
// expiration,
// isAttachmentContent,
// cancellationToken);
return Task.FromResult<string?>(null);
}
public virtual Task CreateFolderAsync(
string containerName,
string blobName,
CancellationToken cancellationToken = default)
{
// 腾讯云Oss没有目录的概念,新建对象时可以模拟目录
// https://cloud.tencent.com/document/product/436/13324
return Task.CompletedTask;
}
public async virtual Task UploadBlobAsync(
string containerName,
string blobName,
Stream content,
string? contentType = null,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
var objectName = CalculateBlobName(blobName);
CreateBucketIfNotExists(client, bucket);
var putObjectRequest = new PutObjectRequest(bucket, objectName, content);
if (!contentType.IsNullOrWhiteSpace())
{
putObjectRequest.SetRequestHeader("Content-Type", contentType);
}
client.PutObject(putObjectRequest);
}
protected async virtual Task<string?> InternalGeneratePresignedUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
bool isAttachmentContent = true,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
@ -161,38 +211,6 @@ public class TencentBlobProvider : IBlobProvider
return client.GenerateSignURL(preSignatureStruct);
}
public virtual Task CreateFolderAsync(
string containerName,
string blobName,
CancellationToken cancellationToken = default)
{
// 腾讯云Oss没有目录的概念,新建对象时可以模拟目录
// https://cloud.tencent.com/document/product/436/13324
return Task.CompletedTask;
}
public async virtual Task UploadBlobAsync(
string containerName,
string blobName,
Stream content,
string? contentType = null,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
var objectName = CalculateBlobName(blobName);
CreateBucketIfNotExists(client, bucket);
var putObjectRequest = new PutObjectRequest(bucket, objectName, content);
if (!contentType.IsNullOrWhiteSpace())
{
putObjectRequest.SetRequestHeader("Content-Type", contentType);
}
client.PutObject(putObjectRequest);
}
protected async virtual Task<CosXml> CreateClientAsync()
{
return await CosClientFactory.CreateAsync<BlobManagementContainer>();

Loading…
Cancel
Save