Browse Source

feat: Enhance blob management module

- Add the interface for generating download links
- Alibaba Cloud Object Storage adds configuration for the expiration time of pre-signed links
- Tencent Cloud Object Storage adds configuration for the expiration time of pre-signed links
- Add generation of download url feature
- Add the setting item for the expiration time of the generated download url
- Add the interface for parsing file ContentType
- Integrate the MimeCheck module to parse the Content-Type of the file
pull/1489/head
colin 4 months ago
parent
commit
5550ce36f7
  1. 2
      aspnet-core/LINGYUN.MicroService.SingleProject.slnx
  2. 10
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs
  3. 4
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfigurationNames.cs
  4. 2
      aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs
  5. 3
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN.Abp.BlobStoring.Aliyun.csproj
  6. 5
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs
  7. 12
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs
  8. 10
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs
  9. 4
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs
  10. 18
      aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/Microsoft/Extensions/DependencyInjection/AliyunHttpClientFactoryServiceCollectionExtensions.cs
  11. 58
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Aliyun/LINGYUN/Abp/BlobManagement/Aliyun/AliyunBlobProvider.cs
  12. 12
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/Dtos/BlobDownloadByKeyInput.cs
  13. 4
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/IBlobAppService.cs
  14. 2
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/Permissions/BlobManagementPermissionDefinitionProvider.cs
  15. 2
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/Permissions/BlobManagementPermissionNames.cs
  16. 16
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/BlobAppService.cs
  17. 52
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/BlobAppServiceBase.cs
  18. 18
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/BlobWithoutContainerAppService.cs
  19. 13
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/Integration/BlobIntegrationService.cs
  20. 14
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/PublicBlobAppService.cs
  21. 15
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/AbpBlobManagementContentTypeResolveOptions.cs
  22. 33
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/BlobContentTypeResolveContext.cs
  23. 7
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Features/BlobManagementFeatureDefinitionProvider.cs
  24. 4
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Features/BlobManagementFeatureNames.cs
  25. 10
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/IBlobContentTypeResolveContributor.cs
  26. 5
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Localization/Resources/en.json
  27. 5
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Localization/Resources/zh-Hans.json
  28. 11
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Settings/BlobManagementSettingDefinitionProvider.cs
  29. 7
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Settings/BlobManagementSettingNames.cs
  30. 71
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/BlobContentTypeResolver.cs
  31. 24
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/BlobDownloadKeyCacheItem.cs
  32. 87
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/BlobManager.cs
  33. 9
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/IBlobContentTypeResolver.cs
  34. 10
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/IBlobProvider.cs
  35. 13
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.FileSystem/LINGYUN/Abp/BlobManagement/FileSystem/FileSystemBlobProvider.cs
  36. 14
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi/LINGYUN/Abp/BlobManagement/AbpBlobManagementHttpApiModule.cs
  37. 14
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi/LINGYUN/Abp/BlobManagement/BlobController.cs
  38. 24
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi/LINGYUN/Abp/BlobManagement/FileExtensionBlobContentTypeResolveContributor.cs
  39. 3
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/FodyWeavers.xml
  40. 30
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/FodyWeavers.xsd
  41. 25
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/LINGYUN.Abp.BlobManagement.MimeCheck.csproj
  42. 15
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/LINGYUN/Abp/BlobManagement/MimeCheck/AbpBlobManagementMimeCheckModule.cs
  43. 33
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/LINGYUN/Abp/BlobManagement/MimeCheck/MimeCheckBlobContentTypeResolveContributor.cs
  44. 57
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Minio/LINGYUN/Abp/BlobManagement/Minio/MinioBlobProvider.cs
  45. 5
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.SettingManagement/LINGYUN/Abp/BlobManagement/SettingManagement/BlobManagementSettingAppService.cs
  46. 49
      aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Tencent/LINGYUN/Abp/BlobManagement/Tencent/TencentBlobProvider.cs
  47. 1
      aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj
  48. 3
      aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs

2
aspnet-core/LINGYUN.MicroService.SingleProject.slnx

@ -11,6 +11,7 @@
<Folder Name="/framework/alibaba-cloud/">
<Project Path="framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN.Abp.Aliyun.SettingManagement.csproj" />
<Project Path="framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN.Abp.Aliyun.csproj" />
<Project Path="framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN.Abp.BlobStoring.Aliyun.csproj" />
<Project Path="framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN.Abp.Sms.Aliyun.csproj" />
</Folder>
<Folder Name="/framework/authentication/">
@ -211,6 +212,7 @@
<Project Path="modules/blob-management/LINGYUN.Abp.BlobManagement.FileSystem/LINGYUN.Abp.BlobManagement.FileSystem.csproj" />
<Project Path="modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi.Client/LINGYUN.Abp.BlobManagement.HttpApi.Client.csproj" />
<Project Path="modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi/LINGYUN.Abp.BlobManagement.HttpApi.csproj" />
<Project Path="modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/LINGYUN.Abp.BlobManagement.MimeCheck.csproj" Id="bb981955-78c8-45b7-a46a-cc9eefa2166a" />
<Project Path="modules/blob-management/LINGYUN.Abp.BlobManagement.Minio/LINGYUN.Abp.BlobManagement.Minio.csproj" />
<Project Path="modules/blob-management/LINGYUN.Abp.BlobManagement.SettingManagement/LINGYUN.Abp.BlobManagement.SettingManagement.csproj" Id="649cb4c3-e7c9-4736-b254-0899bb26bfcb" />
<Project Path="modules/blob-management/LINGYUN.Abp.BlobManagement.Tencent/LINGYUN.Abp.BlobManagement.Tencent.csproj" />

10
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfiguration.cs

@ -53,6 +53,16 @@ public class TencentBlobProviderConfiguration
}
}
/// <summary>
/// Default value: 600.
/// </summary>
public int PresignedGetExpirySeconds {
get => _containerConfiguration.GetConfigurationOrDefault(TencentBlobProviderConfigurationNames.PresignedGetExpirySeconds, _defaultExpirySeconds);
set => _containerConfiguration.SetConfiguration(TencentBlobProviderConfigurationNames.PresignedGetExpirySeconds, value);
}
private int _defaultExpirySeconds = 600;
private readonly BlobContainerConfiguration _containerConfiguration;
public TencentBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration)

4
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentBlobProviderConfigurationNames.cs

@ -22,4 +22,8 @@ public static class TencentBlobProviderConfigurationNames
/// 创建命名空间时防盗链列表
/// </summary>
public const string CreateBucketReferer = "Tencent:OSS:CreateBucketReferer";
/// <summary>
/// 生成预签名Uri的过期时间(s)
/// </summary>
public const string PresignedGetExpirySeconds = "Tencent:OSS:PresignedGetExpirySeconds";
}

2
aspnet-core/framework/cloud-tencent/LINGYUN.Abp.BlobStoring.Tencent/LINGYUN/Abp/BlobStoring/Tencent/TencentCloudBlobProvider.cs

@ -78,7 +78,7 @@ public class TencentCloudBlobProvider : BlobProviderBase, ITransientDependency
key = blobName, //对象键
httpMethod = "GET", //HTTP 请求方法
isHttps = true, //生成 HTTPS 请求 URL
signDurationSecond = 600, //请求签名时间为600s
signDurationSecond = configuration.PresignedGetExpirySeconds, //请求签名时间为600s
headers = null, //签名中需要校验的 header
queryParameters = null //签名中需要校验的 URL 中请求参数
};

3
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN.Abp.BlobStoring.Aliyun.csproj

@ -4,7 +4,7 @@
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net9.0;net10.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
<AssemblyName>LINGYUN.Abp.BlobStoring.Aliyun</AssemblyName>
<PackageId>LINGYUN.Abp.BlobStoring.Aliyun</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
@ -16,6 +16,7 @@
<ItemGroup>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Volo.Abp.BlobStoring" />
</ItemGroup>

5
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/LINGYUN/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.Aliyun;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.BlobStoring;
using Volo.Abp.Modularity;
@ -9,6 +10,10 @@ namespace LINGYUN.Abp.BlobStoring.Aliyun;
typeof(AbpAliyunModule))]
public class AbpBlobStoringAliyunModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAliyunHttpClient();
}
// 需要时引用配置
//public override void ConfigureServices(ServiceConfigurationContext context)
//{

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

@ -1,8 +1,10 @@
using Aliyun.OSS;
using LINGYUN.Abp.Aliyun.Features;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;
@ -14,13 +16,16 @@ namespace LINGYUN.Abp.BlobStoring.Aliyun;
public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
{
protected IOssClientFactory OssClientFactory { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected IAliyunBlobNameCalculator AliyunBlobNameCalculator { get; }
public AliyunBlobProvider(
IOssClientFactory ossClientFactory,
IHttpClientFactory httpClientFactory,
IAliyunBlobNameCalculator aliyunBlobNameCalculator)
{
OssClientFactory = ossClientFactory;
HttpClientFactory = httpClientFactory;
AliyunBlobNameCalculator = aliyunBlobNameCalculator;
}
@ -55,8 +60,11 @@ public class AliyunBlobProvider : BlobProviderBase, ITransientDependency
return null;
}
var ossObject = ossClient.GetObject(GetBucketName(args), blobName);
return ossObject.Content;
var downloadUri = ossClient.GeneratePresignedUri(GetBucketName(args), blobName);
var httpClient = HttpClientFactory.CreateAliyunHttpClient();
return await httpClient.GetStreamAsync(downloadUri, args.CancellationToken);
}
public override async Task SaveAsync(BlobProviderSaveArgs args)

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

@ -40,6 +40,16 @@ public class AliyunBlobProviderConfiguration
}
}
/// <summary>
/// Default value: 7 * 24 * 3600.
/// </summary>
public int PresignedGetExpirySeconds {
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.PresignedGetExpirySeconds, _defaultExpirySeconds);
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.PresignedGetExpirySeconds, value);
}
private int _defaultExpirySeconds = 7 * 24 * 3600;
private readonly BlobContainerConfiguration _containerConfiguration;
public AliyunBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration)

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

@ -18,4 +18,8 @@ public static class AliyunBlobProviderConfigurationNames
/// 创建命名空间时防盗链列表
/// </summary>
public const string CreateBucketReferer = "Aliyun:OSS:CreateBucketReferer";
/// <summary>
/// 生成预签名Uri的过期时间(s)
/// </summary>
public const string PresignedGetExpirySeconds = "Aliyun:OSS:PresignedGetExpirySeconds";
}

18
aspnet-core/framework/common/LINGYUN.Abp.BlobStoring.Aliyun/Microsoft/Extensions/DependencyInjection/AliyunHttpClientFactoryServiceCollectionExtensions.cs

@ -0,0 +1,18 @@
using System.Net.Http;
namespace Microsoft.Extensions.DependencyInjection;
internal static class AliyunHttpClientFactoryServiceCollectionExtensions
{
private const string HttpClientName = "__AliyunBlobStoringApiClient";
public static IServiceCollection AddAliyunHttpClient(this IServiceCollection services)
{
services.AddHttpClient(HttpClientName);
return services;
}
public static HttpClient CreateAliyunHttpClient(this IHttpClientFactory httpClientFactory)
{
return httpClientFactory.CreateClient(HttpClientName);
}
}

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

@ -7,8 +7,9 @@ using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.BlobStoring;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.BlobManagement.Aliyun;
@ -17,24 +18,30 @@ public class AliyunBlobProvider : IBlobProvider
public const string ProviderName = "Aliyun";
public string Name => ProviderName;
protected IClock Clock { get; }
protected IConfiguration Configuration { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IOssClientFactory OssClientFactory { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected AliyunBlobNamingNormalizer BlobNamingNormalizer { get; }
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; }
public AliyunBlobProvider(
IClock clock,
IConfiguration configuration,
ICurrentTenant currentTenant,
IOssClientFactory ossClientFactory,
IHttpClientFactory httpClientFactory,
AliyunBlobNamingNormalizer blobNamingNormalizer)
AliyunBlobNamingNormalizer blobNamingNormalizer,
IBlobContainerConfigurationProvider configurationProvider)
{
Clock = clock;
Configuration = configuration;
CurrentTenant = currentTenant;
OssClientFactory = ossClientFactory;
HttpClientFactory = httpClientFactory;
BlobNamingNormalizer = blobNamingNormalizer;
ConfigurationProvider = configurationProvider;
}
public async virtual Task CreateContainerAsync(
@ -90,6 +97,29 @@ public class AliyunBlobProvider : IBlobProvider
string containerName,
string blobName,
CancellationToken cancellationToken = default)
{
var configuration = GetBlobConfiguration();
var downloadUrl = await GenerateDownloadUrlAsync(
containerName,
blobName,
TimeSpan.FromSeconds(configuration.PresignedGetExpirySeconds),
cancellationToken);
if (downloadUrl.IsNullOrWhiteSpace())
{
return null;
}
var httpClient = HttpClientFactory.CreateAliyunHttpClient();
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
}
public async virtual Task<string?> GenerateDownloadUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
@ -100,11 +130,7 @@ public class AliyunBlobProvider : IBlobProvider
return null;
}
var downloadUrl = client.GeneratePresignedUri(bucket, objectName);
var httpClient = HttpClientFactory.CreateAliyunHttpClient();
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
return client.GeneratePresignedUri(bucket, objectName, Clock.Now.Add(expiration)).ToString();
}
public virtual Task CreateFolderAsync(
@ -120,7 +146,8 @@ public class AliyunBlobProvider : IBlobProvider
public async virtual Task UploadBlobAsync(
string containerName,
string blobName,
Stream content,
Stream content,
string? contentType = null,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
@ -129,7 +156,20 @@ public class AliyunBlobProvider : IBlobProvider
CreateBucketIfNotExists(client, bucket);
client.PutObject(bucket, objectName, content);
var objectMetadata = new ObjectMetadata();
if (!contentType.IsNullOrWhiteSpace())
{
objectMetadata.ContentType = contentType;
}
client.PutObject(bucket, objectName, content, objectMetadata);
}
protected virtual AliyunBlobProviderConfiguration GetBlobConfiguration()
{
var configuration = ConfigurationProvider.Get<BlobManagementContainer>();
var blobConfiguration = configuration.GetAliyunConfiguration();
return blobConfiguration;
}
protected async virtual Task<IOss> CreateClientAsync()

12
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/Dtos/BlobDownloadByKeyInput.cs

@ -0,0 +1,12 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace LINGYUN.Abp.BlobManagement.Dtos;
public class BlobDownloadByKeyInput
{
public Guid? TenantId { get; set; }
[Required]
public string Key { get; set; }
}

4
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/IBlobAppService.cs

@ -17,6 +17,10 @@ public interface IBlobAppService : IApplicationService
Task DeleteAsync(Guid id);
Task<string> GenerateDownloadUrlAsync(Guid id);
Task<IRemoteStreamContent> DownloadAsync(BlobDownloadByKeyInput input);
Task<IRemoteStreamContent> GetContentAsync(Guid id);
Task<IRemoteStreamContent> GetContentByNameAsync(BlobDownloadByNameInput input);

2
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/Permissions/BlobManagementPermissionDefinitionProvider.cs

@ -27,6 +27,8 @@ public class BlobManagementPermissionDefinitionProvider : PermissionDefinitionPr
blob.AddChild(BlobManagementPermissionNames.Blob.Delete, L("Permission:Delete"));
blob.AddChild(BlobManagementPermissionNames.Blob.Download, L("Permission:Download"))
.RequireFeatures(BlobManagementFeatureNames.Blob.DownloadFile);
blob.AddChild(BlobManagementPermissionNames.Blob.GenerateDownloadUrl, L("Permission:GenerateDownloadUrl"))
.RequireFeatures(BlobManagementFeatureNames.Blob.GenerateDownloadUrl);
context.AddResourcePermission(
name: BlobManagementPermissionNames.Blob.Resources.Delete,

2
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application.Contracts/LINGYUN/Abp/BlobManagement/Permissions/BlobManagementPermissionNames.cs

@ -23,6 +23,8 @@ public static class BlobManagementPermissionNames
public const string Download = Default + ".Download";
public const string GenerateDownloadUrl = Default + ".GenerateDownloadUrl";
public const string ManagePermissions = Default + ".ManagePermissions";
public static class Resources

16
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/BlobAppService.cs

@ -1,7 +1,6 @@
using LINGYUN.Abp.BlobManagement.Dtos;
using LINGYUN.Abp.BlobManagement.Features;
using LINGYUN.Abp.BlobManagement.Permissions;
using LINGYUN.Abp.Features.LimitValidation;
using System.Threading.Tasks;
using Volo.Abp.Content;
using Volo.Abp.Features;
@ -23,11 +22,6 @@ public class BlobAppService : BlobAppServiceBase, IBlobAppService
GetListPolicyName = BlobManagementPermissionNames.Blob.Default;
}
[RequiresFeature(BlobManagementFeatureNames.Blob.UploadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.UploadLimit,
BlobManagementFeatureNames.Blob.UploadInterval,
LimitPolicy.Month)]
public async virtual Task CreateChunkFileAsync(BlobFileChunkCreateDto input)
{
await CheckCreatePolicyAsync();
@ -35,11 +29,6 @@ public class BlobAppService : BlobAppServiceBase, IBlobAppService
await base.CreateChunkFileAsync(input.ContainerId.ToString(), input);
}
[RequiresFeature(BlobManagementFeatureNames.Blob.UploadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.UploadLimit,
BlobManagementFeatureNames.Blob.UploadInterval,
LimitPolicy.Month)]
public async virtual Task<BlobDto> CreateFileAsync(BlobFileCreateDto input)
{
await CheckCreatePolicyAsync();
@ -58,11 +47,6 @@ public class BlobAppService : BlobAppServiceBase, IBlobAppService
return await base.CreateFolderAsync(blobContainer, input);
}
[RequiresFeature(BlobManagementFeatureNames.Blob.DownloadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.DownloadLimit,
BlobManagementFeatureNames.Blob.DownloadInterval,
LimitPolicy.Month)]
public async virtual Task<IRemoteStreamContent> GetContentByNameAsync(BlobDownloadByNameInput input)
{
var blobContainer = await BlobContainerRepository.GetByNameAsync(input.ContainerName);

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

@ -1,7 +1,5 @@
using LINGYUN.Abp.BlobManagement.Dtos;
using LINGYUN.Abp.BlobManagement.Features;
using LINGYUN.Abp.BlobManagement.Permissions;
using LINGYUN.Abp.Features.LimitValidation;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
@ -13,7 +11,6 @@ using System.Web;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Content;
using Volo.Abp.Features;
using Volo.Abp.Specifications;
namespace LINGYUN.Abp.BlobManagement;
@ -49,11 +46,44 @@ public abstract class BlobAppServiceBase : BlobManagementApplicationService
await DeleteBlobAsync(blob);
}
[RequiresFeature(BlobManagementFeatureNames.Blob.DownloadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.DownloadLimit,
BlobManagementFeatureNames.Blob.DownloadInterval,
LimitPolicy.Month)]
public async virtual Task<string> GenerateDownloadUrlAsync(Guid id)
{
var blob = await BlobRepository.GetAsync(id);
var blobContainer = await BlobContainerRepository.GetAsync(blob.ContainerId);
await CheckGetPolicyAsync(blob);
var fallbackUrlPrefix = blob.TenantId.HasValue
? $"/api/{BlobManagementRemoteServiceConsts.ModuleName}/blobs/download/t/{blob.TenantId:N}"
: $"/api/{BlobManagementRemoteServiceConsts.ModuleName}/blobs/download";
var downloadUrl = await BlobManager.GenerateDownloadUrlAsync(
blobContainer,
blob,
fallbackUrlPrefix);
return downloadUrl;
}
public async virtual Task<IRemoteStreamContent> DownloadAsync(BlobDownloadByKeyInput input)
{
using (CurrentTenant.Change(input.TenantId))
{
var blob = await BlobManager.FindBlobByDownloadKeyAsync(input.Key);
if (blob == null)
{
return new RemoteStreamContent(Stream.Null, input.Key);
}
// 临时下载链接一般用于临时预览与下载, 过期时间短, 不校验权限
// await CheckGetPolicyAsync(blob);
var stream = await BlobManager.DownloadBlobsync(blob);
return new RemoteStreamContent(stream ?? Stream.Null, blob.Name, blob.ContentType, stream?.Length);
}
}
public async virtual Task<IRemoteStreamContent> GetContentAsync(Guid id)
{
var blob = await BlobRepository.GetAsync(id);
@ -62,7 +92,7 @@ public abstract class BlobAppServiceBase : BlobManagementApplicationService
var stream = await BlobManager.DownloadBlobsync(blob);
return new RemoteStreamContent(stream ?? Stream.Null, blob.Name, blob.ContentType, blob.Size);
return new RemoteStreamContent(stream ?? Stream.Null, blob.Name, blob.ContentType, stream?.Length);
}
public async virtual Task<BlobDto> GetAsync(Guid id)
@ -120,7 +150,7 @@ public abstract class BlobAppServiceBase : BlobManagementApplicationService
blobStream.Length,
parentBlob);
blob = await BlobManager.UploadBlobAsync(blobContainer, blob, blobStream, input.File.ContentType, input.CompareMd5);
blob = await BlobManager.UploadBlobAsync(blobContainer, blob, blobStream, input.CompareMd5);
return ObjectMapper.Map<Blob, BlobDto>(blob);
}
@ -167,7 +197,7 @@ public abstract class BlobAppServiceBase : BlobManagementApplicationService
var stream = await BlobManager.DownloadBlobsync(blob);
return new RemoteStreamContent(stream ?? Stream.Null, blob.Name, blob.ContentType, blob.Size);
return new RemoteStreamContent(stream ?? Stream.Null, blob.Name, blob.ContentType, stream?.Length);
}
protected async virtual Task<Blob?> FindBlobByNameAsync(BlobContainer blobContainer, string name)

18
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/BlobWithoutContainerAppService.cs

@ -1,10 +1,7 @@
using LINGYUN.Abp.BlobManagement.Dtos;
using LINGYUN.Abp.BlobManagement.Features;
using LINGYUN.Abp.Features.LimitValidation;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Content;
using Volo.Abp.Features;
namespace LINGYUN.Abp.BlobManagement;
@ -20,11 +17,6 @@ public abstract class BlobWithoutContainerAppService : BlobAppServiceBase
{
}
[RequiresFeature(BlobManagementFeatureNames.Blob.UploadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.UploadLimit,
BlobManagementFeatureNames.Blob.UploadInterval,
LimitPolicy.Month)]
public async virtual Task CreateChunkFileAsync(BlobFileChunkCreateWithoutContainerDto input)
{
await CheckCreatePolicyAsync();
@ -32,11 +24,6 @@ public abstract class BlobWithoutContainerAppService : BlobAppServiceBase
await base.CreateChunkFileAsync(ContainerName, input);
}
[RequiresFeature(BlobManagementFeatureNames.Blob.UploadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.UploadLimit,
BlobManagementFeatureNames.Blob.UploadInterval,
LimitPolicy.Month)]
public async virtual Task<BlobDto> CreateFileAsync(BlobFileCreateWithoutContainerDto input)
{
await CheckCreatePolicyAsync();
@ -55,11 +42,6 @@ public abstract class BlobWithoutContainerAppService : BlobAppServiceBase
return await base.CreateFolderAsync(blobContainer, input);
}
[RequiresFeature(BlobManagementFeatureNames.Blob.DownloadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.DownloadLimit,
BlobManagementFeatureNames.Blob.DownloadInterval,
LimitPolicy.Month)]
public async virtual Task<IRemoteStreamContent> GetContentByNameAsync(string name)
{
var blobContainer = await BlobContainerRepository.GetByNameAsync(ContainerName);

13
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/Integration/BlobIntegrationService.cs

@ -1,7 +1,6 @@
using LINGYUN.Abp.BlobManagement.Dtos;
using LINGYUN.Abp.BlobManagement.Features;
using LINGYUN.Abp.BlobManagement.Integration.Dtos;
using LINGYUN.Abp.Features.LimitValidation;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
@ -27,11 +26,6 @@ public class BlobIntegrationService : BlobAppServiceBase, IBlobIntegrationServic
{
}
[RequiresFeature(BlobManagementFeatureNames.Blob.UploadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.UploadLimit,
BlobManagementFeatureNames.Blob.UploadInterval,
LimitPolicy.Month)]
public async virtual Task<BlobDto> CreateAsync(BlobFileCreateIntegrationDto input)
{
var blobContainer = await BlobContainerRepository.GetByNameAsync(input.ContainerName);
@ -62,7 +56,7 @@ public class BlobIntegrationService : BlobAppServiceBase, IBlobIntegrationServic
parentBlob,
input.Overwrite == true);
await BlobManager.UploadBlobAsync(blobContainer, blob, blobStream, input.File.ContentType);
await BlobManager.UploadBlobAsync(blobContainer, blob, blobStream);
return ObjectMapper.Map<Blob, BlobDto>(blob);
}
@ -96,11 +90,6 @@ public class BlobIntegrationService : BlobAppServiceBase, IBlobIntegrationServic
}
}
[RequiresFeature(BlobManagementFeatureNames.Blob.DownloadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.DownloadLimit,
BlobManagementFeatureNames.Blob.DownloadInterval,
LimitPolicy.Month)]
public async virtual Task<IRemoteStreamContent> GetContentAsync(BlobFileGetByNameIntegrationDto input)
{
try

14
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Application/LINGYUN/Abp/BlobManagement/PublicBlobAppService.cs

@ -1,4 +1,5 @@
using LINGYUN.Abp.BlobManagement.Features;
using LINGYUN.Abp.BlobManagement.Permissions;
using System.Threading.Tasks;
using Volo.Abp.Features;
@ -15,16 +16,19 @@ public class PublicBlobAppService : BlobWithoutContainerAppService, IPublicBlobA
BlobManager blobManager)
: base(blobContainerRepository, blobRepository, blobManager)
{
DeletePolicyName = BlobManagementPermissionNames.Blob.Delete;
}
protected override Task CheckGetPolicyAsync(string containerName, Blob blob)
protected async override Task CheckGetPolicyAsync(string containerName, Blob blob)
{
// 公共文件访问不校验权限
return Task.CompletedTask;
if (!CurrentUser.IsAuthenticated)
{
await FeatureChecker.CheckEnabledAsync(BlobManagementFeatureNames.PublicAccess);
}
}
protected override Task CheckDeletePolicyAsync(string containerName, Blob blob)
protected async override Task CheckDeletePolicyAsync(string containerName, Blob blob)
{
return base.CheckDeletePolicyAsync(blob);
await CheckPolicyAsync(DeletePolicyName);
}
}

15
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/AbpBlobManagementContentTypeResolveOptions.cs

@ -0,0 +1,15 @@
using JetBrains.Annotations;
using System.Collections.Generic;
namespace LINGYUN.Abp.BlobManagement;
public class AbpBlobManagementContentTypeResolveOptions
{
[NotNull]
public List<IBlobContentTypeResolveContributor> BlobContentTypeResolvers { get; }
public AbpBlobManagementContentTypeResolveOptions()
{
BlobContentTypeResolvers = new List<IBlobContentTypeResolveContributor>();
}
}

33
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/BlobContentTypeResolveContext.cs

@ -0,0 +1,33 @@
using System;
using System.IO;
namespace LINGYUN.Abp.BlobManagement;
public class BlobContentTypeResolveContext
{
public IServiceProvider ServiceProvider { get; }
public string BlobName { get; }
public Stream Stream { get; }
public bool Handled { get; private set; }
public string? ContentType { get; private set; }
public BlobContentTypeResolveContext(
IServiceProvider serviceProvider,
string blobName,
Stream stream)
{
ServiceProvider = serviceProvider;
BlobName = blobName;
Stream = stream;
}
public void Success(string contentType)
{
ContentType = contentType;
Handled = true;
}
public bool HasResolvedContentType()
{
return Handled && !ContentType.IsNullOrWhiteSpace();
}
}

7
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Features/BlobManagementFeatureDefinitionProvider.cs

@ -32,7 +32,12 @@ public class BlobManagementFeatureDefinitionProvider : FeatureDefinitionProvider
displayName: L("Features:DisplayName:EnableBlob"),
description: L("Features:Description:EnableBlob"),
valueType: new ToggleStringValueType(new BooleanValueValidator()));
blobDefaultFeature.CreateChild(
name: BlobManagementFeatureNames.Blob.GenerateDownloadUrl,
defaultValue: true.ToString(),
displayName: L("Features:DisplayName:GenerateDownloadUrl"),
description: L("Features:Description:GenerateDownloadUrl"),
valueType: new ToggleStringValueType(new BooleanValueValidator()));
blobDefaultFeature.CreateChild(
name: BlobManagementFeatureNames.Blob.DownloadFile,
defaultValue: true.ToString(),

4
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Features/BlobManagementFeatureNames.cs

@ -20,6 +20,10 @@ public static class BlobManagementFeatureNames
/// </summary>
public const string Enable = Default + ".Enable";
/// <summary>
/// 生成临时文件下载链接
/// </summary>
public const string GenerateDownloadUrl = Default + ".GenerateDownloadUrl";
/// <summary>
/// 下载文件功能
/// </summary>
public const string DownloadFile = Default + ".DownloadFile";

10
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/IBlobContentTypeResolveContributor.cs

@ -0,0 +1,10 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.BlobManagement;
public interface IBlobContentTypeResolveContributor
{
string Name { get; }
int Priority { get; }
Task ResolveAsync(BlobContentTypeResolveContext context);
}

5
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Localization/Resources/en.json

@ -10,6 +10,7 @@
"Permission:Download": "Download",
"Permission:Blob:Delete": "Delete Blob",
"Permission:Blob:View": "View Blob",
"Permission:GenerateDownloadUrl": "Generate Download Url",
"Features:BlobManagement": "Blob Management",
"Features:DisplayName:EnableBlobManagement": "Enable Blob Management",
"Features:Description:EnableBlobManagement": "Enable to give the application the ability to have an blob storage module.",
@ -29,6 +30,8 @@
"Features:Description:UploadLimit": "Limits the total number of file uploads within a specified period",
"Features:DisplayName:UploadInterval": "Limit Uploads Interval",
"Features:Description:UploadInterval": "The update period that limits the number of files uploaded. The time scale is: month. Default: 1",
"Features:DisplayName:GenerateDownloadUrl": "Generate Download Url",
"Features:Description:GenerateDownloadUrl": "Whether to allow users to generate download links for temporary files",
"BlobManagement:001001": "A blob container named {Name} already exists!",
"BlobManagement:001002": "The blob container is not empty and cannot be deleted!",
"BlobManagement:001003": "The blob container is the system container and cannot be deleted!",
@ -55,6 +58,8 @@
"Description:FileLimitLength": "Limit size of uploaded file in MB",
"DisplayName:AllowFileExtensions": "File extension",
"Description:AllowFileExtensions": "List of allowed extensions to upload files, with multiple extensions separated by, don't need a notation",
"DisplayName:GenerateDownloadUrlExpirySeconds": "Download Url Expiry Seconds",
"Description:GenerateDownloadUrlExpirySeconds": "Set the validity period for the temporary download link (in seconds). If not set or left blank, it defaults to 600 seconds.",
"DisplayName:BlobManagement": "Blob Management",
"DisplayName:Blobs": "Blobs",
"DisplayName:CreationTime": "Creation Time",

5
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Localization/Resources/zh-Hans.json

@ -10,6 +10,7 @@
"Permission:Download": "下载",
"Permission:Blob:Delete": "删除存储对象",
"Permission:Blob:View": "查看存储对象",
"Permission:GenerateDownloadUrl": "生成下载链接",
"Features:BlobManagement": "对象存储",
"Features:DisplayName:EnableBlobManagement": "启用对象存储",
"Features:Description:EnableBlobManagement": "启用以使应用程序拥有对象存储模块的能力.",
@ -29,6 +30,8 @@
"Features:Description:UploadLimit": "在指定的周期内限制上传文件的总次数",
"Features:DisplayName:UploadInterval": "限制上传文件周期",
"Features:Description:UploadInterval": "限制上传文件次数的更新周期,时间刻度为:月,默认:1",
"Features:DisplayName:GenerateDownloadUrl": "生成文件下载链接",
"Features:Description:GenerateDownloadUrl": "是否允许用户生成临时文件下载链接",
"BlobManagement:001001": "已经存在名为 {Name} 的存储容器!",
"BlobManagement:001002": "存储容器非空,不可删除!",
"BlobManagement:001003": "存储容器为系统容器,不可删除!",
@ -55,6 +58,8 @@
"Description:FileLimitLength": "上传文件的限制大小,单位(MB)",
"DisplayName:AllowFileExtensions": "文件扩展名",
"Description:AllowFileExtensions": "允许的上传文件扩展名列表,多个扩展名以,分隔,无需输入.符号",
"DisplayName:GenerateDownloadUrlExpirySeconds": "生成临时下载链接有效期",
"Description:GenerateDownloadUrlExpirySeconds": "生成临时下载链接有效时间, 单位(s), 未设置或为空时默认600s.",
"DisplayName:BlobManagement": "对象存储",
"DisplayName:Blobs": "存储管理",
"DisplayName:CreationTime": "创建时间",

11
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Settings/BlobManagementSettingDefinitionProvider.cs

@ -37,6 +37,17 @@ public class BlobManagementSettingDefinitionProvider : SettingDefinitionProvider
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
name: BlobManagementSettingNames.GenerateDownloadUrlExpirySeconds,
defaultValue: BlobManagementSettingNames.DefaultGenerateDownloadUrlExpirySeconds.ToString(),
displayName: L("DisplayName:GenerateDownloadUrlExpirySeconds"),
description: L("Description:GenerateDownloadUrlExpirySeconds"),
isVisibleToClients: true)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
};
}

7
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain.Shared/LINGYUN/Abp/BlobManagement/Settings/BlobManagementSettingNames.cs

@ -11,7 +11,12 @@ public static class BlobManagementSettingNames
/// 允许的文件扩展名类型
/// </summary>
public const string AllowFileExtensions = GroupName + ".AllowFileExtensions";
/// <summary>
/// 生成临时下载链接有效期(s)
/// </summary>
public const string GenerateDownloadUrlExpirySeconds = GroupName + ".GenerateDownloadUrlExpirySeconds";
public const long DefaultFileLimitLength = 5L;
public const string DefaultAllowFileExtensions = "dll,zip,rar,txt,log,xml,config,json,jpeg,jpg,png,bmp,ico,xlsx,xltx,xls,xlt,docs,dots,doc,dot,pptx,potx,ppt,pot,chm";
public const double DefaultGenerateDownloadUrlExpirySeconds = 600d;
public const string DefaultAllowFileExtensions = "dll,zip,rar,txt,log,xml,config,json,jpeg,jpg,png,bmp,ico,xlsx,xltx,xls,xlt,docs,dots,doc,dot,pdf,pptx,potx,ppt,pot,chm";
}

71
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/BlobContentTypeResolver.cs

@ -0,0 +1,71 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BlobManagement;
public class BlobContentTypeResolver : IBlobContentTypeResolver, ITransientDependency
{
public ILogger<BlobContentTypeResolver> Logger { get; set; }
protected IServiceScopeFactory ServiceScopeFactory { get; }
protected AbpBlobManagementContentTypeResolveOptions Options { get; }
public BlobContentTypeResolver(
IOptions<AbpBlobManagementContentTypeResolveOptions> options,
IServiceScopeFactory serviceScopeFactory)
{
ServiceScopeFactory = serviceScopeFactory;
Options = options.Value;
Logger = NullLogger<BlobContentTypeResolver>.Instance;
}
public async virtual Task<string> ResolveContentTypeAsync(string blobName, Stream blobStream)
{
string? result = null;
Logger.LogDebug("Starting resolving content type...");
using (var serviceScope = ServiceScopeFactory.CreateScope())
{
var context = new BlobContentTypeResolveContext(serviceScope.ServiceProvider, blobName, blobStream);
foreach (var blobContentTypeResolver in Options.BlobContentTypeResolvers.OrderByDescending(x => x.Priority))
{
var originalPosition = context.Stream.CanSeek ? context.Stream.Position : -1;
Logger.LogDebug("Trying to resolve content type '{Name}'...", blobContentTypeResolver.Name);
await blobContentTypeResolver.ResolveAsync(context);
if (originalPosition >= 0 && context.Stream.CanSeek)
{
context.Stream.Position = originalPosition;
}
if (context.HasResolvedContentType())
{
result = context.ContentType;
Logger.LogDebug("Content type resolved by '{Name}' as '{ContentType}'.", blobContentTypeResolver.Name, result);
break;
}
}
}
if (result.IsNullOrWhiteSpace())
{
Logger.LogWarning("No provider can resolve content type. Using the default \"application/octet-stream\".");
return "application/octet-stream";
}
return result;
}
}

24
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/BlobDownloadKeyCacheItem.cs

@ -0,0 +1,24 @@
using System;
namespace LINGYUN.Abp.BlobManagement;
public class BlobDownloadKeyCacheItem
{
public string Url { get; set; }
public Guid BlobId { get; set; }
public BlobDownloadKeyCacheItem()
{
}
public BlobDownloadKeyCacheItem(Guid blobId, string url)
{
BlobId = blobId;
Url = url;
}
public static string CalculateCacheKey(Guid blobId, string blobName)
{
return $"{blobId:N}{blobName.RemovePreFix("/")}";
}
}

87
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/BlobManager.cs

@ -1,4 +1,7 @@
using LINGYUN.Abp.BlobManagement.Settings;
using LINGYUN.Abp.BlobManagement.Features;
using LINGYUN.Abp.BlobManagement.Settings;
using LINGYUN.Abp.Features.LimitValidation;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.IO;
@ -10,6 +13,7 @@ using Volo.Abp.Caching;
using Volo.Abp.Content;
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Distributed;
using Volo.Abp.Features;
using Volo.Abp.IO;
using Volo.Abp.Settings;
using Volo.Abp.Specifications;
@ -19,11 +23,13 @@ namespace LINGYUN.Abp.BlobManagement;
public class BlobManager : DomainService
{
private readonly IDistributedCache<BlobDownloadKeyCacheItem> _blobDownloadKeyCache;
private readonly IDistributedCache<BlobDownloadCacheItem> _blobDownloadCache;
private readonly IDistributedEventBus _distributedEventBus;
private readonly IMemoryCache _blobFileValidateCache;
private readonly ISettingProvider _settingProvider;
private readonly IBlobContentTypeResolver _blobContentTypeResolver;
private readonly IBlobProvider _blobProvider;
private readonly IBlobRepository _blobRepository;
private readonly IBlobContainerRepository _blobContainerRepository;
@ -36,7 +42,9 @@ public class BlobManager : DomainService
IBlobContainerRepository blobContainerRepository,
ICancellationTokenProvider cancellationTokenProvider,
IDistributedEventBus distributedEventBus,
IDistributedCache<BlobDownloadKeyCacheItem> blobDownloadKeyCache,
IDistributedCache<BlobDownloadCacheItem> blobDownloadCache,
IBlobContentTypeResolver blobContentTypeResolver,
IApplicationInfoAccessor applicationInfoAccessor,
IMemoryCache blobFileValidateCache,
ISettingProvider settingProvider)
@ -46,7 +54,9 @@ public class BlobManager : DomainService
_blobContainerRepository = blobContainerRepository;
_cancellationTokenProvider = cancellationTokenProvider;
_distributedEventBus = distributedEventBus;
_blobDownloadKeyCache = blobDownloadKeyCache;
_blobDownloadCache = blobDownloadCache;
_blobContentTypeResolver = blobContentTypeResolver;
_applicationInfoAccessor = applicationInfoAccessor;
_blobFileValidateCache = blobFileValidateCache;
_settingProvider = settingProvider;
@ -183,11 +193,15 @@ public class BlobManager : DomainService
}
}
[RequiresFeature(BlobManagementFeatureNames.Blob.UploadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.UploadLimit,
BlobManagementFeatureNames.Blob.UploadInterval,
LimitPolicy.Month)]
public async virtual Task<Blob> UploadBlobAsync(
BlobContainer blobContainer,
Blob blob,
Stream stream,
string? contentType = null,
string? compareMd5 = null)
{
await ValidateUploadFileAsync(blob.Name, stream.Length);
@ -204,7 +218,14 @@ public class BlobManager : DomainService
blob.SetContentMd5(blobMd5);
}
await _blobProvider.UploadBlobAsync(blobContainer.Name, blob.FullName, stream, GetCancellationToken());
var contentType = await _blobContentTypeResolver.ResolveContentTypeAsync(blob.Name, stream);
await _blobProvider.UploadBlobAsync(
blobContainer.Name,
blob.FullName,
stream,
contentType,
GetCancellationToken());
blob.SetProvider(_blobProvider.Name);
blob.SetFileInfo(contentType, stream.Length);
@ -212,6 +233,7 @@ public class BlobManager : DomainService
return blob;
}
[RequiresFeature(BlobManagementFeatureNames.Blob.UploadFile)]
public async virtual Task CreateChunkBlobAsync(
string containerIdOrName,
string name,
@ -285,6 +307,7 @@ public class BlobManager : DomainService
// 多分片文件流合并上传
mergeFileStream.Seek(0, SeekOrigin.Begin);
await UploadBlobAsync(blobContainer, blob, mergeFileStream, compareMd5);
}
// 文件保存之后删除临时文件目录
@ -299,6 +322,11 @@ public class BlobManager : DomainService
}
}
[RequiresFeature(BlobManagementFeatureNames.Blob.DownloadFile)]
[RequiresLimitFeature(
BlobManagementFeatureNames.Blob.DownloadLimit,
BlobManagementFeatureNames.Blob.DownloadInterval,
LimitPolicy.Month)]
public async virtual Task<Stream?> DownloadBlobsync(Blob blob)
{
var containerName = await _blobContainerRepository.GetNameAsync(blob.ContainerId);
@ -306,6 +334,59 @@ public class BlobManager : DomainService
return await DownloadBlobsync(containerName, blob.FullName);
}
[RequiresFeature(BlobManagementFeatureNames.Blob.GenerateDownloadUrl)]
public async virtual Task<string> GenerateDownloadUrlAsync(
BlobContainer blobContainer,
Blob blob,
string fallbackUrlPrefix)
{
var cacheKey = BlobDownloadKeyCacheItem.CalculateCacheKey(blob.Id, blob.FullName);
var cacheItem = await _blobDownloadKeyCache.GetAsync(cacheKey);
if (cacheItem == null)
{
var downloadUrlExpirySeconds = await _settingProvider.GetAsync(
BlobManagementSettingNames.GenerateDownloadUrlExpirySeconds,
BlobManagementSettingNames.DefaultGenerateDownloadUrlExpirySeconds);
var downloadUrl = await _blobProvider.GenerateDownloadUrlAsync(
blobContainer.Name,
blob.FullName,
TimeSpan.FromSeconds(downloadUrlExpirySeconds),
GetCancellationToken());
if (downloadUrl.IsNullOrWhiteSpace())
{
// 特殊对象存储提供者(FileSystem)无法生成下载链接, 回退指定的请求Url前缀
downloadUrl = fallbackUrlPrefix.EnsureEndsWith('/') + cacheKey;
}
cacheItem = new BlobDownloadKeyCacheItem(
blob.Id,
downloadUrl);
await _blobDownloadKeyCache.SetAsync(
cacheKey,
cacheItem,
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(downloadUrlExpirySeconds),
});
}
return cacheItem.Url;
}
public async virtual Task<Blob?> FindBlobByDownloadKeyAsync(string downloadKey)
{
var cacheItem = await _blobDownloadKeyCache.GetAsync(downloadKey);
if (cacheItem == null)
{
return null;
}
return await _blobRepository.FindAsync(cacheItem.BlobId);
}
public virtual string GetBlobProvider()
{
return _blobProvider.Name;

9
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/IBlobContentTypeResolver.cs

@ -0,0 +1,9 @@
using System.IO;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BlobManagement;
public interface IBlobContentTypeResolver
{
Task<string> ResolveContentTypeAsync(string blobName, Stream blobStream);
}

10
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Domain/LINGYUN/Abp/BlobManagement/IBlobProvider.cs

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -21,6 +22,12 @@ public interface IBlobProvider
string blobName,
CancellationToken cancellationToken = default);
Task<string?> GenerateDownloadUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
CancellationToken cancellationToken = default);
Task CreateFolderAsync(
string containerName,
string blobName,
@ -35,6 +42,7 @@ public interface IBlobProvider
string containerName,
string blobName,
Stream stream,
string? contentType = null,
CancellationToken cancellationToken = default);
Task DeleteBlobAsync(

13
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.FileSystem/LINGYUN/Abp/BlobManagement/FileSystem/FileSystemBlobProvider.cs

@ -99,6 +99,16 @@ public class FileSystemBlobProvider : IBlobProvider
return Task.FromResult<Stream?>(File.OpenRead(fileName));
}
public virtual Task<string?> GenerateDownloadUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
CancellationToken cancellationToken = default)
{
// 本地文件系统无需生成下载链接
return Task.FromResult<string?>(null);
}
public virtual Task CreateFolderAsync(
string containerName,
string blobName,
@ -114,7 +124,8 @@ public class FileSystemBlobProvider : IBlobProvider
public async virtual Task UploadBlobAsync(
string containerName,
string blobName,
Stream content,
Stream content,
string? contentType = null,
CancellationToken cancellationToken = default)
{
var fileName = CalculateBlobName(NormalizeContainerName(containerName.RemovePostFix("/")), blobName);

14
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi/LINGYUN/Abp/BlobManagement/AbpBlobManagementHttpApiModule.cs

@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.Localization;
using Volo.Abp.AspNetCore.VirtualFileSystem;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.BlobManagement;
@ -26,4 +27,17 @@ public class AbpBlobManagementHttpApiModule : AbpModule
typeof(AbpBlobManagementApplicationContractsModule).Assembly);
});
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpAspNetCoreContentOptions>(options =>
{
options.ContentTypeMaps.TryAdd(".log", "text/plain");
options.ContentTypeMaps.TryAdd(".text", "text/plain");
});
Configure<AbpBlobManagementContentTypeResolveOptions>(options =>
{
options.BlobContentTypeResolvers.Add(new FileExtensionBlobContentTypeResolveContributor());
});
}
}

14
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi/LINGYUN/Abp/BlobManagement/BlobController.cs

@ -73,4 +73,18 @@ public class BlobController : BlobControllerBase, IBlobAppService
{
return _service.GetListAsync(input);
}
[HttpGet("{id}/generate-url")]
public virtual Task<string> GenerateDownloadUrlAsync(Guid id)
{
return _service.GenerateDownloadUrlAsync(id);
}
[HttpGet]
[Route("download/{Key}")]
[Route("download/t/{TenantId}/{Key}")]
public virtual Task<IRemoteStreamContent> DownloadAsync(BlobDownloadByKeyInput input)
{
return _service.DownloadAsync(input);
}
}

24
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.HttpApi/LINGYUN/Abp/BlobManagement/FileExtensionBlobContentTypeResolveContributor.cs

@ -0,0 +1,24 @@
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BlobManagement;
public class FileExtensionBlobContentTypeResolveContributor : IBlobContentTypeResolveContributor
{
public string Name => "FileExtension";
public int Priority => 0;
public virtual Task ResolveAsync(BlobContentTypeResolveContext context)
{
var contentTypeProvider = context.ServiceProvider.GetService<IContentTypeProvider>();
if (contentTypeProvider != null &&
contentTypeProvider.TryGetContentType(context.BlobName, out var contentType) &&
!contentType.IsNullOrWhiteSpace())
{
context.Success(contentType);
}
return Task.CompletedTask;
}
}

3
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

25
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/LINGYUN.Abp.BlobManagement.MimeCheck.csproj

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.BlobManagement.MimeCheck</AssemblyName>
<PackageId>LINGYUN.Abp.BlobManagement.MimeCheck</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<Nullable>enable</Nullable>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MimeCheck" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.BlobManagement.Domain\LINGYUN.Abp.BlobManagement.Domain.csproj" />
</ItemGroup>
</Project>

15
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/LINGYUN/Abp/BlobManagement/MimeCheck/AbpBlobManagementMimeCheckModule.cs

@ -0,0 +1,15 @@
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.BlobManagement.MimeCheck;
[DependsOn(typeof(AbpBlobManagementDomainModule))]
public class AbpBlobManagementMimeCheckModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpBlobManagementContentTypeResolveOptions>(options =>
{
options.BlobContentTypeResolvers.Add(new MimeCheckBlobContentTypeResolveContributor());
});
}
}

33
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.MimeCheck/LINGYUN/Abp/BlobManagement/MimeCheck/MimeCheckBlobContentTypeResolveContributor.cs

@ -0,0 +1,33 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MimeCheck.Detection;
using MimeCheck.Validation;
using System;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BlobManagement.MimeCheck;
public class MimeCheckBlobContentTypeResolveContributor : IBlobContentTypeResolveContributor
{
public string Name => "MimeCheck";
public int Priority => 200;
public async virtual Task ResolveAsync(BlobContentTypeResolveContext context)
{
try
{
var streamResult = await MimeValidator.DetectAsync(context.Stream);
if (!streamResult.MimeType.IsNullOrWhiteSpace() &&
!string.Equals(streamResult.MimeType, DetectionResult.Unknown.MimeType))
{
context.Success(streamResult.MimeType);
}
}
catch (Exception ex)
{
context.ServiceProvider
.GetService<ILogger<MimeCheckBlobContentTypeResolveContributor>>()
?.LogWarning("Failed to decetion the blob mime type: {message}!", ex.Message);
}
}
}

57
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.Minio/LINGYUN/Abp/BlobManagement/Minio/MinioBlobProvider.cs

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
using Volo.Abp.BlobStoring.Minio;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.BlobManagement.Minio;
@ -19,17 +20,20 @@ public class MinioBlobProvider : IBlobProvider
public const string ProviderName = "Minio";
public string Name => ProviderName;
protected IClock Clock { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IHttpClientFactory HttpClientFactory { get; }
protected MinioBlobNamingNormalizer BlobNamingNormalizer { get; }
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; }
public MinioBlobProvider(
IClock clock,
ICurrentTenant currentTenant,
IHttpClientFactory httpClientFactory,
MinioBlobNamingNormalizer blobNamingNormalizer,
IBlobContainerConfigurationProvider configurationProvider)
{
Clock = clock;
CurrentTenant = currentTenant;
HttpClientFactory = httpClientFactory;
BlobNamingNormalizer = blobNamingNormalizer;
@ -101,6 +105,29 @@ public class MinioBlobProvider : IBlobProvider
string containerName,
string blobName,
CancellationToken cancellationToken = default)
{
var configuration = GetBlobConfiguration();
var downloadUrl = await GenerateDownloadUrlAsync(
containerName,
blobName,
TimeSpan.FromSeconds(configuration.PresignedGetExpirySeconds),
cancellationToken);
if (downloadUrl.IsNullOrWhiteSpace())
{
return null;
}
var httpClient = HttpClientFactory.CreateMinioHttpClient();
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
}
public async virtual Task<string?> GenerateDownloadUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
CancellationToken cancellationToken = default)
{
var client = GetMinioClient();
var bucket = NormalizeContainerName(containerName);
@ -111,17 +138,11 @@ public class MinioBlobProvider : IBlobProvider
return null;
}
var configuration = GetBlobConfiguration();
var downloadUrl = await client.PresignedGetObjectAsync(
new PresignedGetObjectArgs()
.WithBucket(bucket)
.WithObject(objectName)
.WithExpiry(configuration.PresignedGetExpirySeconds));
var httpClient = HttpClientFactory.CreateMinioHttpClient();
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
return await client.PresignedGetObjectAsync(
new PresignedGetObjectArgs()
.WithBucket(bucket)
.WithObject(objectName)
.WithExpiry(expiration.TotalSeconds.To<int>()));
}
public virtual Task CreateFolderAsync(
@ -136,7 +157,8 @@ public class MinioBlobProvider : IBlobProvider
public async virtual Task UploadBlobAsync(
string containerName,
string blobName,
Stream content,
Stream content,
string? contentType = null,
CancellationToken cancellationToken = default)
{
var client = GetMinioClient();
@ -145,12 +167,17 @@ public class MinioBlobProvider : IBlobProvider
await CreateBucketIfNotExists(client, bucket);
await client.PutObjectAsync(new PutObjectArgs()
var putObjectArgs = new PutObjectArgs()
.WithBucket(bucket)
.WithObject(objectName)
.WithStreamData(content)
.WithObjectSize(content.Length),
cancellationToken);
.WithObjectSize(content.Length);
if (!contentType.IsNullOrWhiteSpace())
{
putObjectArgs.WithContentType(contentType);
}
await client.PutObjectAsync(putObjectArgs, cancellationToken);
}
protected virtual IMinioClient GetMinioClient()

5
aspnet-core/modules/blob-management/LINGYUN.Abp.BlobManagement.SettingManagement/LINGYUN/Abp/BlobManagement/SettingManagement/BlobManagementSettingAppService.cs

@ -51,6 +51,11 @@ public class BlobManagementSettingAppService : ApplicationService, IBlobManageme
var ossObjectSetting = ossSettingGroup.AddSetting(L["DisplayName:Blobs"], L["DisplayName:Blobs"]);
ossObjectSetting.AddDetail(
await SettingDefinitionManager.GetAsync(BlobManagementSettingNames.GenerateDownloadUrlExpirySeconds),
StringLocalizerFactory,
await SettingManager.GetOrNullAsync(BlobManagementSettingNames.GenerateDownloadUrlExpirySeconds, providerName, providerKey),
ValueType.Number);
ossObjectSetting.AddDetail(
await SettingDefinitionManager.GetAsync(BlobManagementSettingNames.FileLimitLength),
StringLocalizerFactory,

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

@ -10,9 +10,9 @@ using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.BlobStoring;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.BlobManagement.Tencent;
@ -21,6 +21,7 @@ public class TencentBlobProvider : IBlobProvider
public const string ProviderName = "TencentCloud";
public string Name => ProviderName;
protected IClock Clock { get; }
protected IConfiguration Configuration { get; }
protected ICurrentTenant CurrentTenant { get; }
protected ICosClientFactory CosClientFactory { get; }
@ -29,6 +30,7 @@ public class TencentBlobProvider : IBlobProvider
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; }
public TencentBlobProvider(
IClock clock,
IConfiguration configuration,
ICurrentTenant currentTenant,
ICosClientFactory cosClientFactory,
@ -36,6 +38,7 @@ public class TencentBlobProvider : IBlobProvider
TencentBlobNamingNormalizer blobNamingNormalizer,
IBlobContainerConfigurationProvider configurationProvider)
{
Clock = clock;
Configuration = configuration;
CurrentTenant = currentTenant;
CosClientFactory = cosClientFactory;
@ -97,6 +100,29 @@ public class TencentBlobProvider : IBlobProvider
string containerName,
string blobName,
CancellationToken cancellationToken = default)
{
var configuration = await GetBlobConfiguration();
var downloadUrl = await GenerateDownloadUrlAsync(
containerName,
blobName,
TimeSpan.FromSeconds(configuration.PresignedGetExpirySeconds),
cancellationToken);
if (downloadUrl.IsNullOrWhiteSpace())
{
return null;
}
var httpClient = HttpClientFactory.CreateTencentHttpClient();
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
}
public async virtual Task<string?> GenerateDownloadUrlAsync(
string containerName,
string blobName,
TimeSpan expiration,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
var bucket = NormalizeContainerName(containerName);
@ -108,8 +134,6 @@ public class TencentBlobProvider : IBlobProvider
}
var configuration = await GetBlobConfiguration();
var blobProviderConfig = Configuration.GetSection($"BlobManagement:{ProviderName}");
// See: https://cloud.tencent.com/document/product/436/47238
var preSignatureStruct = new PreSignatureStruct
{
@ -119,16 +143,12 @@ public class TencentBlobProvider : IBlobProvider
key = objectName, //对象键
httpMethod = "GET", //HTTP 请求方法
isHttps = true, //生成 HTTPS 请求 URL
signDurationSecond = blobProviderConfig.GetValue("SignDurationSecond", 600), //请求签名时间
signDurationSecond = expiration.TotalSeconds.To<long>(), //请求签名时间
headers = null, //签名中需要校验的 header
queryParameters = null //签名中需要校验的 URL 中请求参数
};
var downloadUrl = client.GenerateSignURL(preSignatureStruct);
var httpClient = HttpClientFactory.CreateTencentHttpClient();
return await httpClient.GetStreamAsync(downloadUrl, cancellationToken);
return client.GenerateSignURL(preSignatureStruct);
}
public virtual Task CreateFolderAsync(
@ -144,7 +164,8 @@ public class TencentBlobProvider : IBlobProvider
public async virtual Task UploadBlobAsync(
string containerName,
string blobName,
Stream content,
Stream content,
string? contentType = null,
CancellationToken cancellationToken = default)
{
var client = await CreateClientAsync();
@ -153,7 +174,13 @@ public class TencentBlobProvider : IBlobProvider
CreateBucketIfNotExists(client, bucket);
client.PutObject(new PutObjectRequest(bucket, objectName, content));
var putObjectRequest = new PutObjectRequest(bucket, objectName, content);
if (!contentType.IsNullOrWhiteSpace())
{
putObjectRequest.SetRequestHeader("Content-Type", contentType);
}
client.PutObject(putObjectRequest);
}
protected async virtual Task<CosXml> CreateClientAsync()

1
aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj

@ -151,6 +151,7 @@
<ProjectReference Include="..\..\modules\blob-management\LINGYUN.Abp.BlobManagement.Domain\LINGYUN.Abp.BlobManagement.Domain.csproj" />
<ProjectReference Include="..\..\modules\blob-management\LINGYUN.Abp.BlobManagement.FileSystem\LINGYUN.Abp.BlobManagement.FileSystem.csproj" />
<ProjectReference Include="..\..\modules\blob-management\LINGYUN.Abp.BlobManagement.HttpApi\LINGYUN.Abp.BlobManagement.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\blob-management\LINGYUN.Abp.BlobManagement.MimeCheck\LINGYUN.Abp.BlobManagement.MimeCheck.csproj" />
<ProjectReference Include="..\..\modules\blob-management\LINGYUN.Abp.BlobManagement.Minio\LINGYUN.Abp.BlobManagement.Minio.csproj" />
<ProjectReference Include="..\..\modules\blob-management\LINGYUN.Abp.BlobManagement.Tencent\LINGYUN.Abp.BlobManagement.Tencent.csproj" />
<ProjectReference Include="..\..\modules\blob-management\LINGYUN.Abp.BlobManagement.SettingManagement\LINGYUN.Abp.BlobManagement.SettingManagement.csproj" />

3
aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs

@ -1,3 +1,4 @@
using LINGYUN.Abp.BlobManagement.MimeCheck;
using LINGYUN.Abp.Exporter.MiniSoftware;
using LINGYUN.Abp.Notifications.Calendar;
@ -130,6 +131,8 @@ namespace LY.MicroService.Applications.Single;
typeof(AbpBlobManagementMinioModule),
// 对象存储模块 腾讯云
typeof(AbpBlobManagementTencentModule),
// 对象存储模块 MimeCheck扩展
typeof(AbpBlobManagementMimeCheckModule),
// 对象存储模块 应用服务
typeof(AbpBlobManagementDomainModule),
// 对象存储模块 控制器

Loading…
Cancel
Save