From 0fef9234536fbdadfbf53b29eafb952bf02aad53 Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Mon, 13 Jul 2020 14:11:51 +0800 Subject: [PATCH 01/10] feat: add volo.abp.blobstoring.aliyun from dev --- framework/Volo.Abp.sln | 36 ++++--- .../FodyWeavers.xml | 3 + .../FodyWeavers.xsd | 30 ++++++ .../Volo.Abp.BlobStoring.Aliyun.csproj | 27 ++++++ .../Aliyun/AbpBlobStoringAliyunModule.cs | 17 ++++ ...yunBlobContainerConfigurationExtensions.cs | 25 +++++ .../Aliyun/AliyunBlobNamingNormalizer.cs | 56 +++++++++++ .../BlobStoring/Aliyun/AliyunBlobProvider.cs | 81 ++++++++++++++++ .../Aliyun/AliyunBlobProviderConfiguration.cs | 96 +++++++++++++++++++ .../AliyunBlobProviderConfigurationNames.cs | 15 +++ .../Aliyun/DefaultAliyunBlobNameCalculator.cs | 22 +++++ .../Aliyun/DefaultOssClientFactory.cs | 77 +++++++++++++++ .../Aliyun/IAliyunBlobNameCalculator.cs | 7 ++ .../BlobStoring/Aliyun/IOssClientFactory.cs | 9 ++ .../Properties/serviceDependencies.json | 7 ++ .../Properties/serviceDependencies.local.json | 7 ++ .../Volo.Abp.BlobStoring.Aliyun.Tests.csproj | 20 ++++ .../Aliyun/AbpBlobStoringAliyunTestBase.cs | 19 ++++ .../Aliyun/AbpBlobStoringAliyunTestModule.cs | 65 +++++++++++++ .../Aliyun/AliyunBlobContainer_Tests.cs | 34 +++++++ .../Aliyun/AliyunBlobNameCalculator_Tests.cs | 55 +++++++++++ ...liyunBlobNamingNormalizerProvider_Tests.cs | 57 +++++++++++ .../Abp/BlobStoring/Aliyun/MyTestContainer.cs | 7 ++ nupkg/common.ps1 | 1 + 24 files changed, 762 insertions(+), 11 deletions(-) create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xsd create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo.Abp.BlobStoring.Aliyun.csproj create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainerConfigurationExtensions.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNameCalculator.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IAliyunBlobNameCalculator.cs create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IOssClientFactory.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestBase.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNameCalculator_Tests.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs create mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index a3ee894572..f68c47790f 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -289,27 +289,31 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR.Tests", "test\Volo.Abp.AspNetCore.SignalR.Tests\Volo.Abp.AspNetCore.SignalR.Tests.csproj", "{8B758716-DCC9-4223-8421-5588D1597487}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj", "{79323211-E658-493E-9863-035AA4C3F913}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj", "{79323211-E658-493E-9863-035AA4C3F913}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring", "src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj", "{A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring", "src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj", "{A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Tests", "test\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj", "{D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Tests", "test\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj", "{D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem", "src\Volo.Abp.BlobStoring.FileSystem\Volo.Abp.BlobStoring.FileSystem.csproj", "{02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.FileSystem", "src\Volo.Abp.BlobStoring.FileSystem\Volo.Abp.BlobStoring.FileSystem.csproj", "{02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem.Tests", "test\Volo.Abp.BlobStoring.FileSystem.Tests\Volo.Abp.BlobStoring.FileSystem.Tests.csproj", "{68443D4A-1608-4039-B995-7AF4CF82E9F8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.FileSystem.Tests", "test\Volo.Abp.BlobStoring.FileSystem.Tests\Volo.Abp.BlobStoring.FileSystem.Tests.csproj", "{68443D4A-1608-4039-B995-7AF4CF82E9F8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle.Devart", "src\Volo.Abp.EntityFrameworkCore.Oracle.Devart\Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj", "{75E5C841-5F36-4C44-A532-57CB8E7FFE15}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Oracle.Devart", "src\Volo.Abp.EntityFrameworkCore.Oracle.Devart\Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj", "{75E5C841-5F36-4C44-A532-57CB8E7FFE15}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure", "src\Volo.Abp.BlobStoring.Azure\Volo.Abp.BlobStoring.Azure.csproj", "{C44242F7-D55D-4867-AAF4-A786E404312E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Azure", "src\Volo.Abp.BlobStoring.Azure\Volo.Abp.BlobStoring.Azure.csproj", "{C44242F7-D55D-4867-AAF4-A786E404312E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure.Tests", "test\Volo.Abp.BlobStoring.Azure.Tests\Volo.Abp.BlobStoring.Azure.Tests.csproj", "{A80E9A0B-8932-4B5D-83FB-6751708FD484}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Azure.Tests", "test\Volo.Abp.BlobStoring.Azure.Tests\Volo.Abp.BlobStoring.Azure.Tests.csproj", "{A80E9A0B-8932-4B5D-83FB-6751708FD484}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle", "src\Volo.Abp.EntityFrameworkCore.Oracle\Volo.Abp.EntityFrameworkCore.Oracle.csproj", "{1738845A-5348-4EB8-B736-CD1D22A808B4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Oracle", "src\Volo.Abp.EntityFrameworkCore.Oracle\Volo.Abp.EntityFrameworkCore.Oracle.csproj", "{1738845A-5348-4EB8-B736-CD1D22A808B4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis", "src\Volo.Abp.Caching.StackExchangeRedis\Volo.Abp.Caching.StackExchangeRedis.csproj", "{2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Caching.StackExchangeRedis", "src\Volo.Abp.Caching.StackExchangeRedis\Volo.Abp.Caching.StackExchangeRedis.csproj", "{2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis.Tests", "test\Volo.Abp.Caching.StackExchangeRedis.Tests\Volo.Abp.Caching.StackExchangeRedis.Tests.csproj", "{60D0E384-965E-4F81-9D71-B28F419254FC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Caching.StackExchangeRedis.Tests", "test\Volo.Abp.Caching.StackExchangeRedis.Tests\Volo.Abp.Caching.StackExchangeRedis.Tests.csproj", "{60D0E384-965E-4F81-9D71-B28F419254FC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Aliyun", "src\Volo.Abp.BlobStoring.Aliyun\Volo.Abp.BlobStoring.Aliyun.csproj", "{98AC8166-6F07-45D4-9695-961B78E00611}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Aliyun.Tests", "test\Volo.Abp.BlobStoring.Aliyun.Tests\Volo.Abp.BlobStoring.Aliyun.Tests.csproj", "{B823E35A-96C4-4E1A-83A7-6BC52407D83D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -925,6 +929,14 @@ Global {60D0E384-965E-4F81-9D71-B28F419254FC}.Debug|Any CPU.Build.0 = Debug|Any CPU {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.Build.0 = Release|Any CPU + {98AC8166-6F07-45D4-9695-961B78E00611}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {98AC8166-6F07-45D4-9695-961B78E00611}.Debug|Any CPU.Build.0 = Debug|Any CPU + {98AC8166-6F07-45D4-9695-961B78E00611}.Release|Any CPU.ActiveCfg = Release|Any CPU + {98AC8166-6F07-45D4-9695-961B78E00611}.Release|Any CPU.Build.0 = Release|Any CPU + {B823E35A-96C4-4E1A-83A7-6BC52407D83D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B823E35A-96C4-4E1A-83A7-6BC52407D83D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B823E35A-96C4-4E1A-83A7-6BC52407D83D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B823E35A-96C4-4E1A-83A7-6BC52407D83D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1082,6 +1094,8 @@ Global {1738845A-5348-4EB8-B736-CD1D22A808B4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {60D0E384-965E-4F81-9D71-B28F419254FC} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {98AC8166-6F07-45D4-9695-961B78E00611} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {B823E35A-96C4-4E1A-83A7-6BC52407D83D} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml new file mode 100644 index 0000000000..00e1d9a1c1 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xsd b/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo.Abp.BlobStoring.Aliyun.csproj b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo.Abp.BlobStoring.Aliyun.csproj new file mode 100644 index 0000000000..c9b29dc83f --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo.Abp.BlobStoring.Aliyun.csproj @@ -0,0 +1,27 @@ + + + + + + + netstandard2.0 + Volo.Abp.BlobStoring.Aliyun + Volo.Abp.BlobStoring.Aliyun + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs new file mode 100644 index 0000000000..6472413ebb --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs @@ -0,0 +1,17 @@ +using Volo.Abp.Caching; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + /// + /// https://help.aliyun.com/document_detail/31817.html + /// + [DependsOn( + typeof(AbpBlobStoringModule), + typeof(AbpCachingModule) + )] + public class AbpBlobStoringAliyunModule: AbpModule + { + + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..7802173b23 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainerConfigurationExtensions.cs @@ -0,0 +1,25 @@ +using System; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public static class AliyunBlobContainerConfigurationExtensions + { + public static AliyunBlobProviderConfiguration GetAliyunConfiguration( + this BlobContainerConfiguration containerConfiguration) + { + return new AliyunBlobProviderConfiguration(containerConfiguration); + } + + public static BlobContainerConfiguration UseAliyun( + this BlobContainerConfiguration containerConfiguration, + Action aliyunConfigureAction) + { + containerConfiguration.ProviderType = typeof(AliyunBlobProvider); + containerConfiguration.NamingNormalizers.TryAdd(); + + aliyunConfigureAction(new AliyunBlobProviderConfiguration(containerConfiguration)); + + return containerConfiguration; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs new file mode 100644 index 0000000000..b7fda66b0e --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs @@ -0,0 +1,56 @@ +using System.Text.RegularExpressions; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class AliyunBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency + { + /// + /// 只允许小写字母、数字、短横线(-),且不能以短横线开头或结尾 + /// Container names can contain only letters, numbers, and the dash (-) character + /// can't start or end with the dash (-) character + /// 3~63 个字符 + /// Container names must be from 3 through 63 characters long + /// + public virtual string NormalizeContainerName(string containerName) + { + // All letters in a container name must be lowercase. + containerName = containerName.ToLower(); + + // Container names can contain only letters, numbers, and the dash (-) character. + containerName = Regex.Replace(containerName, "[^a-z0-9-]", string.Empty); + + // Every dash (-) character must be immediately preceded and followed by a letter or number; + // consecutive dashes are not permitted in container names. + // Container names must start or end with a letter or number + containerName = Regex.Replace(containerName, "-{2,}", "-"); + containerName = Regex.Replace(containerName, "^-", string.Empty); + containerName = Regex.Replace(containerName, "-$", string.Empty); + + // Container names must be from 3 through 63 characters long. + if (containerName.Length < 3) + { + var length = containerName.Length; + for (var i = 0; i < 3 - length; i++) + { + containerName += "0"; + } + } + + if (containerName.Length > 63) + { + containerName = containerName.Substring(0, 63); + } + + // can't start or end with the dash (-) character + containerName = containerName.Trim('-'); + + return containerName; + } + + public virtual string NormalizeBlobName(string blobName) + { + return blobName; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs new file mode 100644 index 0000000000..ce558bef0f --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs @@ -0,0 +1,81 @@ +using Aliyun.OSS; +using System.IO; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class AliyunBlobProvider : BlobProviderBase, ITransientDependency + { + protected IOssClientFactory OssClientFactory { get; } + protected IAliyunBlobNameCalculator AliyunBlobNameCalculator { get; } + + public AliyunBlobProvider( + IOssClientFactory ossClientFactory, + IAliyunBlobNameCalculator aliyunBlobNameCalculator) + { + OssClientFactory = ossClientFactory; + AliyunBlobNameCalculator = aliyunBlobNameCalculator; + } + + private IOss GetOssClient(BlobContainerConfiguration blobContainerConfiguration) + { + var aliyunConfig = blobContainerConfiguration.GetAliyunConfiguration(); + return OssClientFactory.Create(aliyunConfig); + } + + private IOss GetOssClient(AliyunBlobProviderConfiguration aliyunConfig) + { + return OssClientFactory.Create(aliyunConfig); + } + + public override async Task SaveAsync(BlobProviderSaveArgs args) + { + var blobName = AliyunBlobNameCalculator.Calculate(args); + if (!args.OverrideExisting && await ExistsAsync(new BlobProviderExistsArgs(args.ContainerName, args.Configuration, args.BlobName))) + { + throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); + } + var aliyunConfig = args.Configuration.GetAliyunConfiguration(); + var OssClient = GetOssClient(aliyunConfig); + if (aliyunConfig.CreateContainerIfNotExists) + { + if (!OssClient.DoesBucketExist(args.ContainerName)) + { + OssClient.CreateBucket(args.ContainerName); + } + } + OssClient.PutObject(args.ContainerName, blobName, args.BlobStream); + } + + public override Task DeleteAsync(BlobProviderDeleteArgs args) + { + var blobName = AliyunBlobNameCalculator.Calculate(args); + var OssClient = GetOssClient(args.Configuration); + var result = OssClient.DeleteObject(args.ContainerName, blobName); + //TODO: undifend delete flag + //https://help.aliyun.com/document_detail/91924.html + return Task.FromResult(true); + } + + public override Task ExistsAsync(BlobProviderExistsArgs args) + { + var blobName = AliyunBlobNameCalculator.Calculate(args); + var OssClient = GetOssClient(args.Configuration); + return Task.FromResult(OssClient.DoesObjectExist(args.ContainerName, blobName)); + } + + public override async Task GetOrNullAsync(BlobProviderGetArgs args) + { + var blobName = AliyunBlobNameCalculator.Calculate(args); + + var OssClient = GetOssClient(args.Configuration); + if (!await ExistsAsync(new BlobProviderExistsArgs(args.ContainerName, args.Configuration, args.BlobName))) + { + return null; + } + var result = OssClient.GetObject(args.ContainerName, blobName); + return result.Content; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs new file mode 100644 index 0000000000..90cbe1912b --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs @@ -0,0 +1,96 @@ +using System; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + /// + /// STS临时授权访问OSS:https://help.aliyun.com/document_detail/100624.html + /// + public class AliyunBlobProviderConfiguration + { + public string AccessKeyId + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.AccessKeyId); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.AccessKeyId, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + public string AccessKeySecret + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.AccessKeySecret); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.AccessKeySecret, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + /// + /// https://help.aliyun.com/document_detail/31837.html + /// eg: https://oss-cn-beijing.aliyuncs.com + /// + public string Endpoint + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + /// + /// STS https://help.aliyun.com/document_detail/66053.html + /// eg:cn-beijing + /// + public string RegionId + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.RegionId); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RegionId, value); + } + + /// + /// eg:acs:ram::$accountID:role/$roleName + /// + public string RoleArn + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.RoleArn); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RoleArn, value); + } + + public string RoleSessionName + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.RoleSessionName); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RoleSessionName, value); + } + + public int DurationSeconds + { + get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.DurationSeconds, 0); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.DurationSeconds, value); + } + + public string Policy + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.Policy); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Policy, value); + } + + /// + /// Default value: false. + /// + public bool CreateContainerIfNotExists + { + get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.CreateContainerIfNotExists, false); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.CreateContainerIfNotExists, value); + } + + private readonly BlobContainerConfiguration _containerConfiguration; + + public AliyunBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) + { + _containerConfiguration = containerConfiguration; + } + + public string ToOssKeyString() + { + Uri uPoint = new Uri(Endpoint); + return $"memorycache:aliyun:id:{AccessKeyId},sec:{AccessKeySecret},ept:{uPoint.Host.ToLower()}"; + } + + public string ToOssWithStsKeyString() + { + return ToOssKeyString() + $",rid:{RegionId},ra:{RoleArn},rsn:{RoleSessionName},pl:{Policy}"; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs new file mode 100644 index 0000000000..c2b0e5ea7b --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs @@ -0,0 +1,15 @@ +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class AliyunBlobProviderConfigurationNames + { + public const string AccessKeyId = "Aliyun.AccessKeyId"; + public const string AccessKeySecret = "Aliyun.AccessKeySecret"; + public const string Endpoint = "Aliyun.Endpoint"; + public const string RegionId = "Aliyun.RegionId"; + public const string RoleArn = "Aliyun.RoleArn"; + public const string RoleSessionName = "Aliyun.RoleSessionName"; + public const string DurationSeconds = "Aliyun.DurationSeconds"; + public const string Policy = "Aliyun.Policy"; + public const string CreateContainerIfNotExists = "Aliyun.CreateContainerIfNotExists"; + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNameCalculator.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNameCalculator.cs new file mode 100644 index 0000000000..e6b3e85ff6 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNameCalculator.cs @@ -0,0 +1,22 @@ +using Volo.Abp.DependencyInjection; +using Volo.Abp.MultiTenancy; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class DefaultAliyunBlobNameCalculator: IAliyunBlobNameCalculator, ITransientDependency + { + protected ICurrentTenant CurrentTenant { get; } + + public DefaultAliyunBlobNameCalculator(ICurrentTenant currentTenant) + { + CurrentTenant = currentTenant; + } + + public virtual string Calculate(BlobProviderArgs args) + { + return CurrentTenant.Id == null + ? $"host/{args.BlobName}" + : $"tenants/{CurrentTenant.Id.Value:D}/{args.BlobName}"; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs new file mode 100644 index 0000000000..a794d8c25a --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs @@ -0,0 +1,77 @@ +using Aliyun.Acs.Core; +using Aliyun.Acs.Core.Auth.Sts; +using Aliyun.Acs.Core.Http; +using Aliyun.Acs.Core.Profile; +using Aliyun.OSS; +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + /// + /// STS:https://help.aliyun.com/document_detail/28756.html + /// STS or sub account number + /// + public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency + { + private readonly IBlobContainerConfigurationProvider _configurationProvider; + private readonly IMemoryCache _cache; + public DefaultOssClientFactory( + IBlobContainerConfigurationProvider configurationProvider, + IMemoryCache cache) + { + _configurationProvider = configurationProvider; + _cache = cache; + } + + public virtual IOss Create(AliyunBlobProviderConfiguration aliyunConfig) + { + //使用账号 sub account number + if (aliyunConfig.DurationSeconds <= 0) + { + var key = aliyunConfig.ToOssKeyString(); + var iOssClient = _cache.Get(key); + if (iOssClient != null) + { + return iOssClient; + } + iOssClient = new OssClient(aliyunConfig.Endpoint, aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); + _cache.Set(key, iOssClient); + return iOssClient; + } + else + { + //使用STS + var key = aliyunConfig.ToOssWithStsKeyString(); + var iOssClient = _cache.Get(key); + if (iOssClient != null) + { + return iOssClient; + } + IClientProfile profile = DefaultProfile.GetProfile( + aliyunConfig.RegionId, + aliyunConfig.AccessKeyId, + aliyunConfig.AccessKeySecret); + DefaultAcsClient client = new DefaultAcsClient(profile); + //构建AssumeRole请求 + AssumeRoleRequest request = new AssumeRoleRequest + { + AcceptFormat = FormatType.JSON, + //指定角色ARN + RoleArn = aliyunConfig.RoleArn, + RoleSessionName = aliyunConfig.RoleSessionName, + //设置Token有效期,可选参数,默认3600秒 + DurationSeconds = aliyunConfig.DurationSeconds, + //设置Token的附加权限策略;在获取Token时,通过额外设置一个权限策略进一步减小Token的权限 + Policy = aliyunConfig.Policy.IsNullOrEmpty() ? null : aliyunConfig.Policy, + }; + var response = client.GetAcsResponse(request); + iOssClient = new OssClient(aliyunConfig.Endpoint, response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken); + _cache.Set(key, iOssClient, TimeSpan.FromSeconds(aliyunConfig.DurationSeconds)); + return iOssClient; + } + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IAliyunBlobNameCalculator.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IAliyunBlobNameCalculator.cs new file mode 100644 index 0000000000..755e6beaca --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IAliyunBlobNameCalculator.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Aliyun +{ + public interface IAliyunBlobNameCalculator + { + string Calculate(BlobProviderArgs args); + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IOssClientFactory.cs new file mode 100644 index 0000000000..8410383012 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/IOssClientFactory.cs @@ -0,0 +1,9 @@ +using Aliyun.OSS; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public interface IOssClientFactory + { + IOss Create(AliyunBlobProviderConfiguration args); + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json new file mode 100644 index 0000000000..a4e7aa3d33 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "secrets1": { + "type": "secrets" + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json new file mode 100644 index 0000000000..09b109bc6f --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "secrets1": { + "type": "secrets.user" + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj new file mode 100644 index 0000000000..c9ba95211c --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj @@ -0,0 +1,20 @@ + + + + + + netcoreapp3.1 + + fe9a87da-3584-40e0-a06c-aa499936015d + + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestBase.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestBase.cs new file mode 100644 index 0000000000..bc696ad877 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestBase.cs @@ -0,0 +1,19 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class AbpBlobStoringAliyunTestCommonBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } + public class AbpBlobStoringAliyunTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs new file mode 100644 index 0000000000..4a63887fda --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs @@ -0,0 +1,65 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using System; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + + /// + /// This module will not try to connect to azure. + /// + [DependsOn( + typeof(AbpBlobStoringAliyunModule), + typeof(AbpBlobStoringTestModule) + )] + public class AbpBlobStoringAliyunTestCommonModule : AbpModule + { + + } + + [DependsOn( + typeof(AbpBlobStoringAliyunTestCommonModule) + )] + public class AbpBlobStoringAliyunTestModule : AbpModule + { + private const string UserSecretsId = "fe9a87da-3584-40e0-a06c-aa499936015d"; + + public override void ConfigureServices(ServiceConfigurationContext context) + { + context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(builderAction: builder => + { + builder.AddUserSecrets(UserSecretsId); + })); + + var configuration = context.Services.GetConfiguration(); + var _accessKeyId = configuration["Aliyun:AccessKeyId"]; + var _accessKeySecret = configuration["Aliyun:AccessKeySecret"]; + var _endpoint = configuration["Aliyun:Endpoint"]; + var _regionId = configuration["Aliyun:RegionId"]; + var _roleArn = configuration["Aliyun:RoleArn"]; + + Configure(options => + { + options.Containers.ConfigureAll((containerName, containerConfiguration) => + { + containerConfiguration.UseAliyun(aliyun => + { + aliyun.AccessKeyId = _accessKeyId; + aliyun.AccessKeySecret = _accessKeySecret; + aliyun.Endpoint = _endpoint;//eg:https://oss-cn-beijing.aliyuncs.com + //STS + aliyun.RegionId = _regionId;//eg:cn-beijing + aliyun.RoleArn = _roleArn;//eg:acs:ram::1320235309887297:role/role-oss-xxxxx + aliyun.RoleSessionName = Guid.NewGuid().ToString("N"); + aliyun.DurationSeconds = 3600; + aliyun.Policy = String.Empty; + //Other + aliyun.CreateContainerIfNotExists = true; + }); + }); + }); + } + + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs new file mode 100644 index 0000000000..e4df766706 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs @@ -0,0 +1,34 @@ +using Shouldly; +using System.IO; +using System.Text; +using Xunit; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class AliyunBlobContainer_Tests : BlobContainer_Tests + { + private readonly IBlobContainer _blobContainer; + private readonly string _blobname = "my-blob"; + private readonly string _stringContent = "my-blob-content"; + public AliyunBlobContainer_Tests() + { + _blobContainer = GetRequiredService>(); + } + + [Fact] + public async void BlobContainer_Test() + { + var streamContent = Encoding.Default.GetBytes(_stringContent); + await _blobContainer.SaveAsync(_blobname, streamContent, true); + var streamData = await _blobContainer.GetAsync(_blobname); + var tfExist = await _blobContainer.ExistsAsync(_blobname); + if (tfExist) + { + await _blobContainer.DeleteAsync(_blobname); + } + StreamReader reader = new StreamReader(streamData); + var stringData = reader.ReadToEnd();// Encoding.Default.GetString(bytes); + stringData.ShouldBe(_stringContent); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNameCalculator_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNameCalculator_Tests.cs new file mode 100644 index 0000000000..d7ea806b16 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNameCalculator_Tests.cs @@ -0,0 +1,55 @@ +using Shouldly; +using System; +using Volo.Abp.MultiTenancy; +using Xunit; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class AliyunBlobNameCalculator_Tests: AbpBlobStoringAliyunTestCommonBase + { + private readonly IAliyunBlobNameCalculator _calculator; + private readonly ICurrentTenant _currentTenant; + + private const string AliyunSeparator = "/"; + + public AliyunBlobNameCalculator_Tests() + { + _calculator = GetRequiredService(); + _currentTenant = GetRequiredService(); + } + + [Fact] + public void Default_Settings() + { + _calculator.Calculate( + GetArgs("my-container", "my-blob") + ).ShouldBe($"host{AliyunSeparator}my-blob"); + } + + [Fact] + public void Default_Settings_With_TenantId() + { + var tenantId = Guid.NewGuid(); + + using (_currentTenant.Change(tenantId)) + { + _calculator.Calculate( + GetArgs("my-container", "my-blob") + ).ShouldBe($"tenants{AliyunSeparator}{tenantId:D}{AliyunSeparator}my-blob"); + } + } + + private static BlobProviderArgs GetArgs( + string containerName, + string blobName) + { + return new BlobProviderGetArgs( + containerName, + new BlobContainerConfiguration().UseAliyun(x => + { + }), + blobName + ); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs new file mode 100644 index 0000000000..83a2b4bf9e --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs @@ -0,0 +1,57 @@ +using Shouldly; +using Xunit; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + public class DefaultAliyunBlobNamingNormalizerProvider_Tests: AbpBlobStoringAliyunTestCommonBase + { + private readonly IBlobNamingNormalizer _blobNamingNormalizer; + + public DefaultAliyunBlobNamingNormalizerProvider_Tests() + { + _blobNamingNormalizer = GetRequiredService(); + } + + [Fact] + public void NormalizeContainerName_Lowercase() + { + var filename = "ThisIsMyContainerName"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("thisismycontainername"); + } + + [Fact] + public void NormalizeContainerName_Only_Letters_Numbers_Dash() + { + var filename = ",./this-i,./s-my-c,./ont,./ai+*/.=!@#$n^&*er-name.+/"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container-name"); + } + + [Fact] + public void NormalizeContainerName_Dash() + { + var filename = "-this--is----my-container----name-"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("this-is-my-container-name"); + } + + + [Fact] + public void NormalizeContainerName_Min_Length() + { + var filename = "a"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeGreaterThanOrEqualTo(3); + } + + + [Fact] + public void NormalizeContainerName_Max_Length() + { + var filename = "abpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabp"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.Length.ShouldBeLessThanOrEqualTo(63); + } + } +} diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs new file mode 100644 index 0000000000..71daed58ff --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.Aliyun +{ + [BlobContainerName("my-container")] + public class MyTestContainer + { + } +} diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index d35f1355f5..da7320cfa1 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -58,6 +58,7 @@ $projects = ( "framework/src/Volo.Abp.BackgroundWorkers.Quartz", "framework/src/Volo.Abp.BlobStoring", "framework/src/Volo.Abp.BlobStoring.FileSystem", + "framework/src/Volo.Abp.BlobStoring.Aliyun", "framework/src/Volo.Abp.BlobStoring.Azure", "framework/src/Volo.Abp.Caching", "framework/src/Volo.Abp.Caching.StackExchangeRedis", From c9a36548fdba82d15c23903336962c6d60885064 Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Mon, 13 Jul 2020 14:29:29 +0800 Subject: [PATCH 02/10] chore: translate into english --- .../Aliyun/AliyunBlobNamingNormalizer.cs | 6 +++--- .../Aliyun/AliyunBlobProviderConfiguration.cs | 12 +++++++++++- .../BlobStoring/Aliyun/DefaultOssClientFactory.cs | 14 +++++++------- .../Aliyun/AbpBlobStoringAliyunTestModule.cs | 4 ---- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs index b7fda66b0e..e504e30d77 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs @@ -6,11 +6,11 @@ namespace Volo.Abp.BlobStoring.Aliyun public class AliyunBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency { /// - /// 只允许小写字母、数字、短横线(-),且不能以短横线开头或结尾 /// Container names can contain only letters, numbers, and the dash (-) character - /// can't start or end with the dash (-) character - /// 3~63 个字符 + /// they can't start or end with the dash (-) character /// Container names must be from 3 through 63 characters long + /// 只允许小写字母、数字、短横线(-),且不能以短横线开头或结尾 + /// 3~63 个字符 /// public virtual string NormalizeContainerName(string containerName) { diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs index 90cbe1912b..a4be7cabc2 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs @@ -3,7 +3,9 @@ namespace Volo.Abp.BlobStoring.Aliyun { /// - /// STS临时授权访问OSS:https://help.aliyun.com/document_detail/100624.html + /// Sub-account access to OSS or STS temporary authorization to access OSS + /// 子账号/STS临时授权访问OSS + /// https://help.aliyun.com/document_detail/100624.html /// public class AliyunBlobProviderConfiguration { @@ -48,12 +50,20 @@ namespace Volo.Abp.BlobStoring.Aliyun set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RoleArn, value); } + /// + /// The name used to identify the temporary access credentials, it is recommended to use different application users to distinguish. + /// 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分。 + /// public string RoleSessionName { get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.RoleSessionName); set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RoleSessionName, value); } + /// + /// Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. + /// 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600。 + /// public int DurationSeconds { get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.DurationSeconds, 0); diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs index a794d8c25a..1e379291ed 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs @@ -11,8 +11,9 @@ using Volo.Abp.DependencyInjection; namespace Volo.Abp.BlobStoring.Aliyun { /// + /// Sub-account access to OSS or STS temporary authorization to access OSS + /// 子账号/STS临时授权访问OSS /// STS:https://help.aliyun.com/document_detail/28756.html - /// STS or sub account number /// public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency { @@ -28,7 +29,7 @@ namespace Volo.Abp.BlobStoring.Aliyun public virtual IOss Create(AliyunBlobProviderConfiguration aliyunConfig) { - //使用账号 sub account number + //Sub-account if (aliyunConfig.DurationSeconds <= 0) { var key = aliyunConfig.ToOssKeyString(); @@ -43,7 +44,7 @@ namespace Volo.Abp.BlobStoring.Aliyun } else { - //使用STS + //STS temporary authorization to access OSS var key = aliyunConfig.ToOssWithStsKeyString(); var iOssClient = _cache.Get(key); if (iOssClient != null) @@ -55,16 +56,15 @@ namespace Volo.Abp.BlobStoring.Aliyun aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); DefaultAcsClient client = new DefaultAcsClient(profile); - //构建AssumeRole请求 AssumeRoleRequest request = new AssumeRoleRequest { AcceptFormat = FormatType.JSON, - //指定角色ARN + //eg:acs:ram::$accountID:role/$roleName RoleArn = aliyunConfig.RoleArn, RoleSessionName = aliyunConfig.RoleSessionName, - //设置Token有效期,可选参数,默认3600秒 + //Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. default 3600 DurationSeconds = aliyunConfig.DurationSeconds, - //设置Token的附加权限策略;在获取Token时,通过额外设置一个权限策略进一步减小Token的权限 + //Set additional permission policy of Token; when acquiring Token, further reduce the permission of Token by setting an additional permission policy Policy = aliyunConfig.Policy.IsNullOrEmpty() ? null : aliyunConfig.Policy, }; var response = client.GetAcsResponse(request); diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs index 4a63887fda..f62fe9e4b6 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs @@ -5,10 +5,6 @@ using Volo.Abp.Modularity; namespace Volo.Abp.BlobStoring.Aliyun { - - /// - /// This module will not try to connect to azure. - /// [DependsOn( typeof(AbpBlobStoringAliyunModule), typeof(AbpBlobStoringTestModule) From 15ed1023494bd5af4d27cdd85e38e5ae9866967a Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Mon, 13 Jul 2020 15:11:36 +0800 Subject: [PATCH 03/10] chore: remove unused object --- .../Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs index 1e379291ed..8f068a4822 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs @@ -17,13 +17,10 @@ namespace Volo.Abp.BlobStoring.Aliyun /// public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency { - private readonly IBlobContainerConfigurationProvider _configurationProvider; private readonly IMemoryCache _cache; public DefaultOssClientFactory( - IBlobContainerConfigurationProvider configurationProvider, IMemoryCache cache) { - _configurationProvider = configurationProvider; _cache = cache; } From 372388f314d127f10dc97b32b1d7022d1fe3ddc4 Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Tue, 14 Jul 2020 08:49:37 +0800 Subject: [PATCH 04/10] fix: usersecrets is not set,the unit tests error. --- .../Aliyun/AliyunBlobContainer_Tests.cs | 50 +++++++++---------- ...liyunBlobNamingNormalizerProvider_Tests.cs | 9 ++++ 2 files changed, 34 insertions(+), 25 deletions(-) diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs index e4df766706..f9501baa64 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs @@ -5,30 +5,30 @@ using Xunit; namespace Volo.Abp.BlobStoring.Aliyun { - public class AliyunBlobContainer_Tests : BlobContainer_Tests - { - private readonly IBlobContainer _blobContainer; - private readonly string _blobname = "my-blob"; - private readonly string _stringContent = "my-blob-content"; - public AliyunBlobContainer_Tests() - { - _blobContainer = GetRequiredService>(); - } + //public class AliyunBlobContainer_Tests : BlobContainer_Tests + //{ + // private readonly IBlobContainer _blobContainer; + // private readonly string _blobname = "my-blob"; + // private readonly string _stringContent = "my-blob-content"; + // public AliyunBlobContainer_Tests() + // { + // _blobContainer = GetRequiredService>(); + // } - [Fact] - public async void BlobContainer_Test() - { - var streamContent = Encoding.Default.GetBytes(_stringContent); - await _blobContainer.SaveAsync(_blobname, streamContent, true); - var streamData = await _blobContainer.GetAsync(_blobname); - var tfExist = await _blobContainer.ExistsAsync(_blobname); - if (tfExist) - { - await _blobContainer.DeleteAsync(_blobname); - } - StreamReader reader = new StreamReader(streamData); - var stringData = reader.ReadToEnd();// Encoding.Default.GetString(bytes); - stringData.ShouldBe(_stringContent); - } - } + // [Fact] + // public async void BlobContainer_Test() + // { + // var streamContent = Encoding.Default.GetBytes(_stringContent); + // await _blobContainer.SaveAsync(_blobname, streamContent, true); + // var streamData = await _blobContainer.GetAsync(_blobname); + // var tfExist = await _blobContainer.ExistsAsync(_blobname); + // if (tfExist) + // { + // await _blobContainer.DeleteAsync(_blobname); + // } + // StreamReader reader = new StreamReader(streamData); + // var stringData = reader.ReadToEnd();// Encoding.Default.GetString(bytes); + // stringData.ShouldBe(_stringContent); + // } + //} } diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs index 83a2b4bf9e..eebb43f1c8 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs @@ -53,5 +53,14 @@ namespace Volo.Abp.BlobStoring.Aliyun filename = _blobNamingNormalizer.NormalizeContainerName(filename); filename.Length.ShouldBeLessThanOrEqualTo(63); } + + + [Fact] + public void NormalizeContainerName_Trim() + { + var filename = "-abpabpabpabp-"; + filename = _blobNamingNormalizer.NormalizeContainerName(filename); + filename.ShouldBe("abpabpabpabp"); + } } } From 30f36aca8f2820d4dd7d81b951077829be94084c Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Wed, 15 Jul 2020 18:42:41 +0800 Subject: [PATCH 05/10] chore: resolve conversation https://github.com/abpframework/abp/pull/4735 --- .../FodyWeavers.xml | 2 +- .../Aliyun/AbpBlobStoringAliyunModule.cs | 3 - .../Aliyun/AliyunBlobNamingNormalizer.cs | 5 -- .../BlobStoring/Aliyun/AliyunBlobProvider.cs | 58 +++++++++++----- .../Aliyun/AliyunBlobProviderConfiguration.cs | 39 ++++++----- .../AliyunBlobProviderConfigurationNames.cs | 1 + .../Aliyun/AssumeRoleCredentialsCacheItem.cs | 30 +++++++++ .../Aliyun/DefaultOssClientFactory.cs | 66 +++++++++---------- .../Properties/serviceDependencies.json | 7 -- .../Properties/serviceDependencies.local.json | 7 -- .../Volo.Abp.BlobStoring.Aliyun.Tests.csproj | 4 ++ .../Aliyun/AbpBlobStoringAliyunTestModule.cs | 18 ++++- .../Aliyun/AliyunBlobContainer_Tests.cs | 35 +++------- ...liyunBlobNamingNormalizerProvider_Tests.cs | 8 --- .../Abp/BlobStoring/Aliyun/MyTestContainer.cs | 7 -- 15 files changed, 149 insertions(+), 141 deletions(-) create mode 100644 framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs delete mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json delete mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json delete mode 100644 framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml index 00e1d9a1c1..be0de3a908 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/FodyWeavers.xml @@ -1,3 +1,3 @@  - + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs index 6472413ebb..c14dd193fc 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunModule.cs @@ -3,9 +3,6 @@ using Volo.Abp.Modularity; namespace Volo.Abp.BlobStoring.Aliyun { - /// - /// https://help.aliyun.com/document_detail/31817.html - /// [DependsOn( typeof(AbpBlobStoringModule), typeof(AbpCachingModule) diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs index e504e30d77..b456d7abb6 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobNamingNormalizer.cs @@ -9,8 +9,6 @@ namespace Volo.Abp.BlobStoring.Aliyun /// Container names can contain only letters, numbers, and the dash (-) character /// they can't start or end with the dash (-) character /// Container names must be from 3 through 63 characters long - /// 只允许小写字母、数字、短横线(-),且不能以短横线开头或结尾 - /// 3~63 个字符 /// public virtual string NormalizeContainerName(string containerName) { @@ -42,9 +40,6 @@ namespace Volo.Abp.BlobStoring.Aliyun containerName = containerName.Substring(0, 63); } - // can't start or end with the dash (-) character - containerName = containerName.Trim('-'); - return containerName; } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs index ce558bef0f..a562d93ee3 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs @@ -1,4 +1,5 @@ using Aliyun.OSS; +using System; using System.IO; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -29,30 +30,34 @@ namespace Volo.Abp.BlobStoring.Aliyun return OssClientFactory.Create(aliyunConfig); } - public override async Task SaveAsync(BlobProviderSaveArgs args) + + public override Task SaveAsync(BlobProviderSaveArgs args) { + var containerName = GetContainerName(args); var blobName = AliyunBlobNameCalculator.Calculate(args); - if (!args.OverrideExisting && await ExistsAsync(new BlobProviderExistsArgs(args.ContainerName, args.Configuration, args.BlobName))) + var aliyunConfig = args.Configuration.GetAliyunConfiguration(); + var ossClient = GetOssClient(aliyunConfig); + if (!args.OverrideExisting && BlobExistsAsync(ossClient, containerName, blobName)) { - throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{args.ContainerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); + throw new BlobAlreadyExistsException($"Saving BLOB '{args.BlobName}' does already exists in the container '{containerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); } - var aliyunConfig = args.Configuration.GetAliyunConfiguration(); - var OssClient = GetOssClient(aliyunConfig); if (aliyunConfig.CreateContainerIfNotExists) { - if (!OssClient.DoesBucketExist(args.ContainerName)) + if (!ossClient.DoesBucketExist(containerName)) { - OssClient.CreateBucket(args.ContainerName); + ossClient.CreateBucket(containerName); } } - OssClient.PutObject(args.ContainerName, blobName, args.BlobStream); + ossClient.PutObject(containerName, blobName, args.BlobStream); + return Task.CompletedTask; } public override Task DeleteAsync(BlobProviderDeleteArgs args) { + var containerName = GetContainerName(args); var blobName = AliyunBlobNameCalculator.Calculate(args); - var OssClient = GetOssClient(args.Configuration); - var result = OssClient.DeleteObject(args.ContainerName, blobName); + var ossClient = GetOssClient(args.Configuration); + var result = ossClient.DeleteObject(containerName, blobName); //TODO: undifend delete flag //https://help.aliyun.com/document_detail/91924.html return Task.FromResult(true); @@ -60,22 +65,39 @@ namespace Volo.Abp.BlobStoring.Aliyun public override Task ExistsAsync(BlobProviderExistsArgs args) { + var containerName = GetContainerName(args); var blobName = AliyunBlobNameCalculator.Calculate(args); - var OssClient = GetOssClient(args.Configuration); - return Task.FromResult(OssClient.DoesObjectExist(args.ContainerName, blobName)); + var ossClient = GetOssClient(args.Configuration); + return Task.FromResult(BlobExistsAsync(ossClient, containerName, blobName)); } - public override async Task GetOrNullAsync(BlobProviderGetArgs args) + public override Task GetOrNullAsync(BlobProviderGetArgs args) { + var containerName = GetContainerName(args); var blobName = AliyunBlobNameCalculator.Calculate(args); - - var OssClient = GetOssClient(args.Configuration); - if (!await ExistsAsync(new BlobProviderExistsArgs(args.ContainerName, args.Configuration, args.BlobName))) + var ossClient = GetOssClient(args.Configuration); + if (!BlobExistsAsync(ossClient, containerName, blobName)) { return null; } - var result = OssClient.GetObject(args.ContainerName, blobName); - return result.Content; + var result = ossClient.GetObject(containerName, blobName); + return Task.FromResult(result.Content); } + + private static string GetContainerName(BlobProviderArgs args) + { + var configuration = args.Configuration.GetAliyunConfiguration(); + return configuration.ContainerName.IsNullOrWhiteSpace() + ? args.ContainerName + : configuration.ContainerName; + } + + private bool BlobExistsAsync(IOss ossClient,string containerName, string blobName) + { + // Make sure Blob Container exists. + return ossClient.DoesBucketExist(containerName) && + ossClient.DoesObjectExist(containerName, blobName); + } + } } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs index a4be7cabc2..058f65412c 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfiguration.cs @@ -4,8 +4,6 @@ namespace Volo.Abp.BlobStoring.Aliyun { /// /// Sub-account access to OSS or STS temporary authorization to access OSS - /// 子账号/STS临时授权访问OSS - /// https://help.aliyun.com/document_detail/100624.html /// public class AliyunBlobProviderConfiguration { @@ -21,20 +19,12 @@ namespace Volo.Abp.BlobStoring.Aliyun set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.AccessKeySecret, Check.NotNullOrWhiteSpace(value, nameof(value))); } - /// - /// https://help.aliyun.com/document_detail/31837.html - /// eg: https://oss-cn-beijing.aliyuncs.com - /// public string Endpoint { get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint); set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint, Check.NotNullOrWhiteSpace(value, nameof(value))); } - /// - /// STS https://help.aliyun.com/document_detail/66053.html - /// eg:cn-beijing - /// public string RegionId { get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.RegionId); @@ -42,7 +32,7 @@ namespace Volo.Abp.BlobStoring.Aliyun } /// - /// eg:acs:ram::$accountID:role/$roleName + /// acs:ram::$accountID:role/$roleName /// public string RoleArn { @@ -52,7 +42,6 @@ namespace Volo.Abp.BlobStoring.Aliyun /// /// The name used to identify the temporary access credentials, it is recommended to use different application users to distinguish. - /// 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分。 /// public string RoleSessionName { @@ -62,7 +51,6 @@ namespace Volo.Abp.BlobStoring.Aliyun /// /// Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. - /// 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600。 /// public int DurationSeconds { @@ -70,12 +58,27 @@ namespace Volo.Abp.BlobStoring.Aliyun set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.DurationSeconds, value); } + /// + /// If policy is empty, the user will get all permissions under this role + /// public string Policy { get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.Policy); set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Policy, value); } + /// + /// This name may only contain lowercase letters, numbers, and hyphens, and must begin with a letter or a number. + /// Each hyphen must be preceded and followed by a non-hyphen character. + /// The name must also be between 3 and 63 characters long. + /// If this parameter is not specified, the ContainerName of the will be used. + /// + public string ContainerName + { + get => _containerConfiguration.GetConfiguration(AliyunBlobProviderConfigurationNames.ContainerName); + set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.ContainerName, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + /// /// Default value: false. /// @@ -92,15 +95,11 @@ namespace Volo.Abp.BlobStoring.Aliyun _containerConfiguration = containerConfiguration; } - public string ToOssKeyString() - { - Uri uPoint = new Uri(Endpoint); - return $"memorycache:aliyun:id:{AccessKeyId},sec:{AccessKeySecret},ept:{uPoint.Host.ToLower()}"; - } - public string ToOssWithStsKeyString() + public string ToKeyString() { - return ToOssKeyString() + $",rid:{RegionId},ra:{RoleArn},rsn:{RoleSessionName},pl:{Policy}"; + Uri uPoint = new Uri(Endpoint); + return $"blobstoring:aliyun:id:{AccessKeyId},sec:{AccessKeySecret},ept:{uPoint.Host.ToLower()},rid:{RegionId},ra:{RoleArn},rsn:{RoleSessionName},pl:{Policy}"; } } } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs index c2b0e5ea7b..c889b374cc 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProviderConfigurationNames.cs @@ -10,6 +10,7 @@ public const string RoleSessionName = "Aliyun.RoleSessionName"; public const string DurationSeconds = "Aliyun.DurationSeconds"; public const string Policy = "Aliyun.Policy"; + public const string ContainerName = "Aliyun:ContainerName"; public const string CreateContainerIfNotExists = "Aliyun.CreateContainerIfNotExists"; } } diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs new file mode 100644 index 0000000000..e0d75f2954 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AssumeRoleCredentialsCacheItem.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Volo.Abp.Caching; + +namespace Volo.Abp.BlobStoring.Aliyun +{ + [Serializable] + [CacheName("AssumeRoleCredentials")] + public class AssumeRoleCredentialsCacheItem + { + public string AccessKeyId { get; set; } + + public string AccessKeySecret { get; set; } + + public string SecurityToken { get; set; } + + public AssumeRoleCredentialsCacheItem() + { + + } + + public AssumeRoleCredentialsCacheItem(string accessKeyId,string accessKeySecret,string securityToken) + { + AccessKeyId = accessKeyId; + AccessKeySecret = accessKeySecret; + SecurityToken = securityToken; + } + } +} diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs index 8f068a4822..8b8e466232 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs @@ -3,23 +3,22 @@ using Aliyun.Acs.Core.Auth.Sts; using Aliyun.Acs.Core.Http; using Aliyun.Acs.Core.Profile; using Aliyun.OSS; -using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Caching.Distributed; using System; using System.Collections.Generic; +using Volo.Abp.Caching; using Volo.Abp.DependencyInjection; namespace Volo.Abp.BlobStoring.Aliyun { /// /// Sub-account access to OSS or STS temporary authorization to access OSS - /// 子账号/STS临时授权访问OSS - /// STS:https://help.aliyun.com/document_detail/28756.html /// public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency { - private readonly IMemoryCache _cache; + private readonly IDistributedCache _cache; public DefaultOssClientFactory( - IMemoryCache cache) + IDistributedCache cache) { _cache = cache; } @@ -29,45 +28,40 @@ namespace Volo.Abp.BlobStoring.Aliyun //Sub-account if (aliyunConfig.DurationSeconds <= 0) { - var key = aliyunConfig.ToOssKeyString(); - var iOssClient = _cache.Get(key); - if (iOssClient != null) - { - return iOssClient; - } - iOssClient = new OssClient(aliyunConfig.Endpoint, aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); - _cache.Set(key, iOssClient); - return iOssClient; + return new OssClient(aliyunConfig.Endpoint, aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); } else { //STS temporary authorization to access OSS - var key = aliyunConfig.ToOssWithStsKeyString(); - var iOssClient = _cache.Get(key); - if (iOssClient != null) + var key = aliyunConfig.ToKeyString(); + var cacheItem = _cache.Get(key); + if (cacheItem == null) { - return iOssClient; - } - IClientProfile profile = DefaultProfile.GetProfile( + IClientProfile profile = DefaultProfile.GetProfile( aliyunConfig.RegionId, aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); - DefaultAcsClient client = new DefaultAcsClient(profile); - AssumeRoleRequest request = new AssumeRoleRequest - { - AcceptFormat = FormatType.JSON, - //eg:acs:ram::$accountID:role/$roleName - RoleArn = aliyunConfig.RoleArn, - RoleSessionName = aliyunConfig.RoleSessionName, - //Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. default 3600 - DurationSeconds = aliyunConfig.DurationSeconds, - //Set additional permission policy of Token; when acquiring Token, further reduce the permission of Token by setting an additional permission policy - Policy = aliyunConfig.Policy.IsNullOrEmpty() ? null : aliyunConfig.Policy, - }; - var response = client.GetAcsResponse(request); - iOssClient = new OssClient(aliyunConfig.Endpoint, response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken); - _cache.Set(key, iOssClient, TimeSpan.FromSeconds(aliyunConfig.DurationSeconds)); - return iOssClient; + DefaultAcsClient client = new DefaultAcsClient(profile); + AssumeRoleRequest request = new AssumeRoleRequest + { + AcceptFormat = FormatType.JSON, + //eg:acs:ram::$accountID:role/$roleName + RoleArn = aliyunConfig.RoleArn, + RoleSessionName = aliyunConfig.RoleSessionName, + //Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600. default 3600 + DurationSeconds = aliyunConfig.DurationSeconds, + //Set additional permission policy of Token; when acquiring Token, further reduce the permission of Token by setting an additional permission policy + Policy = aliyunConfig.Policy.IsNullOrEmpty() ? null : aliyunConfig.Policy, + }; + var response = client.GetAcsResponse(request); + cacheItem = new AssumeRoleCredentialsCacheItem(response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken); + _cache.Set(key, cacheItem, new DistributedCacheEntryOptions() + { + //Subtract 10 seconds of network request time. + AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(aliyunConfig.DurationSeconds - 10) + }); + } + return new OssClient(aliyunConfig.Endpoint, cacheItem.AccessKeyId, cacheItem.AccessKeySecret, cacheItem.SecurityToken); } } } diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json deleted file mode 100644 index a4e7aa3d33..0000000000 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": { - "secrets1": { - "type": "secrets" - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json deleted file mode 100644 index 09b109bc6f..0000000000 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Properties/serviceDependencies.local.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "dependencies": { - "secrets1": { - "type": "secrets.user" - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj index c9ba95211c..121832cefb 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs index f62fe9e4b6..6c14cebbb8 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs @@ -21,6 +21,9 @@ namespace Volo.Abp.BlobStoring.Aliyun { private const string UserSecretsId = "fe9a87da-3584-40e0-a06c-aa499936015d"; + private AliyunBlobProviderConfiguration _configuration; + private readonly string _randomContainerName = "abp-aliyun-test-container-" + Guid.NewGuid().ToString("N"); + public override void ConfigureServices(ServiceConfigurationContext context) { context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(builderAction: builder => @@ -43,19 +46,28 @@ namespace Volo.Abp.BlobStoring.Aliyun { aliyun.AccessKeyId = _accessKeyId; aliyun.AccessKeySecret = _accessKeySecret; - aliyun.Endpoint = _endpoint;//eg:https://oss-cn-beijing.aliyuncs.com + aliyun.Endpoint = _endpoint; //STS - aliyun.RegionId = _regionId;//eg:cn-beijing - aliyun.RoleArn = _roleArn;//eg:acs:ram::1320235309887297:role/role-oss-xxxxx + aliyun.RegionId = _regionId; + aliyun.RoleArn = _roleArn; aliyun.RoleSessionName = Guid.NewGuid().ToString("N"); aliyun.DurationSeconds = 3600; aliyun.Policy = String.Empty; //Other aliyun.CreateContainerIfNotExists = true; + aliyun.ContainerName = _randomContainerName; + _configuration = aliyun; }); }); }); } + public override void OnApplicationShutdown(ApplicationShutdownContext context) + { + var ossClientFactory = context.ServiceProvider.GetService(); + var ossClient = ossClientFactory.Create(_configuration); + ossClient.DeleteBucket(_randomContainerName); + } + } } diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs index f9501baa64..29f84091df 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AliyunBlobContainer_Tests.cs @@ -5,30 +5,13 @@ using Xunit; namespace Volo.Abp.BlobStoring.Aliyun { - //public class AliyunBlobContainer_Tests : BlobContainer_Tests - //{ - // private readonly IBlobContainer _blobContainer; - // private readonly string _blobname = "my-blob"; - // private readonly string _stringContent = "my-blob-content"; - // public AliyunBlobContainer_Tests() - // { - // _blobContainer = GetRequiredService>(); - // } - - // [Fact] - // public async void BlobContainer_Test() - // { - // var streamContent = Encoding.Default.GetBytes(_stringContent); - // await _blobContainer.SaveAsync(_blobname, streamContent, true); - // var streamData = await _blobContainer.GetAsync(_blobname); - // var tfExist = await _blobContainer.ExistsAsync(_blobname); - // if (tfExist) - // { - // await _blobContainer.DeleteAsync(_blobname); - // } - // StreamReader reader = new StreamReader(streamData); - // var stringData = reader.ReadToEnd();// Encoding.Default.GetString(bytes); - // stringData.ShouldBe(_stringContent); - // } - //} + /* + //Please set the correct connection string in secrets.json and continue the test. + public class AliyunBlobContainer_Tests : BlobContainer_Tests + { + public AliyunBlobContainer_Tests() + { + } + } + */ } diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs index eebb43f1c8..895c87cc91 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/DefaultAliyunBlobNamingNormalizerProvider_Tests.cs @@ -54,13 +54,5 @@ namespace Volo.Abp.BlobStoring.Aliyun filename.Length.ShouldBeLessThanOrEqualTo(63); } - - [Fact] - public void NormalizeContainerName_Trim() - { - var filename = "-abpabpabpabp-"; - filename = _blobNamingNormalizer.NormalizeContainerName(filename); - filename.ShouldBe("abpabpabpabp"); - } } } diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs deleted file mode 100644 index 71daed58ff..0000000000 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/MyTestContainer.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Volo.Abp.BlobStoring.Aliyun -{ - [BlobContainerName("my-container")] - public class MyTestContainer - { - } -} From 889d9f1910d588cfd2ca2740c43a632cf5d9a870 Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Thu, 16 Jul 2020 13:20:52 +0800 Subject: [PATCH 06/10] fix: Unit test get exceptions. --- .../BlobStoring/Aliyun/AliyunBlobProvider.cs | 18 +++++---- .../Aliyun/DefaultOssClientFactory.cs | 8 ++-- .../Volo.Abp.BlobStoring.Aliyun.Tests.csproj | 2 +- .../Aliyun/AbpBlobStoringAliyunTestModule.cs | 39 ++++++++++++------- 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs index a562d93ee3..237465ef91 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/AliyunBlobProvider.cs @@ -19,13 +19,13 @@ namespace Volo.Abp.BlobStoring.Aliyun AliyunBlobNameCalculator = aliyunBlobNameCalculator; } - private IOss GetOssClient(BlobContainerConfiguration blobContainerConfiguration) + protected virtual IOss GetOssClient(BlobContainerConfiguration blobContainerConfiguration) { var aliyunConfig = blobContainerConfiguration.GetAliyunConfiguration(); return OssClientFactory.Create(aliyunConfig); } - private IOss GetOssClient(AliyunBlobProviderConfiguration aliyunConfig) + protected virtual IOss GetOssClient(AliyunBlobProviderConfiguration aliyunConfig) { return OssClientFactory.Create(aliyunConfig); } @@ -57,9 +57,11 @@ namespace Volo.Abp.BlobStoring.Aliyun var containerName = GetContainerName(args); var blobName = AliyunBlobNameCalculator.Calculate(args); var ossClient = GetOssClient(args.Configuration); - var result = ossClient.DeleteObject(containerName, blobName); - //TODO: undifend delete flag - //https://help.aliyun.com/document_detail/91924.html + if(!BlobExistsAsync(ossClient, containerName, blobName)) + { + return Task.FromResult(false); + } + ossClient.DeleteObject(containerName, blobName); return Task.FromResult(true); } @@ -71,7 +73,7 @@ namespace Volo.Abp.BlobStoring.Aliyun return Task.FromResult(BlobExistsAsync(ossClient, containerName, blobName)); } - public override Task GetOrNullAsync(BlobProviderGetArgs args) + public override async Task GetOrNullAsync(BlobProviderGetArgs args) { var containerName = GetContainerName(args); var blobName = AliyunBlobNameCalculator.Calculate(args); @@ -81,7 +83,9 @@ namespace Volo.Abp.BlobStoring.Aliyun return null; } var result = ossClient.GetObject(containerName, blobName); - return Task.FromResult(result.Content); + var memoryStream = new MemoryStream(); + await result.Content.CopyToAsync(memoryStream); + return memoryStream; } private static string GetContainerName(BlobProviderArgs args) diff --git a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs index 8b8e466232..8f69603f7c 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aliyun/Volo/Abp/BlobStoring/Aliyun/DefaultOssClientFactory.cs @@ -16,11 +16,11 @@ namespace Volo.Abp.BlobStoring.Aliyun /// public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency { - private readonly IDistributedCache _cache; + protected IDistributedCache Cache { get; } public DefaultOssClientFactory( IDistributedCache cache) { - _cache = cache; + Cache = cache; } public virtual IOss Create(AliyunBlobProviderConfiguration aliyunConfig) @@ -34,7 +34,7 @@ namespace Volo.Abp.BlobStoring.Aliyun { //STS temporary authorization to access OSS var key = aliyunConfig.ToKeyString(); - var cacheItem = _cache.Get(key); + var cacheItem = Cache.Get(key); if (cacheItem == null) { IClientProfile profile = DefaultProfile.GetProfile( @@ -55,7 +55,7 @@ namespace Volo.Abp.BlobStoring.Aliyun }; var response = client.GetAcsResponse(request); cacheItem = new AssumeRoleCredentialsCacheItem(response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken); - _cache.Set(key, cacheItem, new DistributedCacheEntryOptions() + Cache.Set(key, cacheItem, new DistributedCacheEntryOptions() { //Subtract 10 seconds of network request time. AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(aliyunConfig.DurationSeconds - 10) diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj index 121832cefb..8c5097bd84 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo.Abp.BlobStoring.Aliyun.Tests.csproj @@ -5,7 +5,7 @@ netcoreapp3.1 - fe9a87da-3584-40e0-a06c-aa499936015d + 9f0d2c00-80c1-435b-bfab-2c39c8249091 diff --git a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs index 6c14cebbb8..1635e3390f 100644 --- a/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs +++ b/framework/test/Volo.Abp.BlobStoring.Aliyun.Tests/Volo/Abp/BlobStoring/Aliyun/AbpBlobStoringAliyunTestModule.cs @@ -1,6 +1,8 @@ -using Microsoft.Extensions.Configuration; +using Aliyun.OSS; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; +using System.Linq; using Volo.Abp.Modularity; namespace Volo.Abp.BlobStoring.Aliyun @@ -19,7 +21,7 @@ namespace Volo.Abp.BlobStoring.Aliyun )] public class AbpBlobStoringAliyunTestModule : AbpModule { - private const string UserSecretsId = "fe9a87da-3584-40e0-a06c-aa499936015d"; + private const string UserSecretsId = "9f0d2c00-80c1-435b-bfab-2c39c8249091"; private AliyunBlobProviderConfiguration _configuration; private readonly string _randomContainerName = "abp-aliyun-test-container-" + Guid.NewGuid().ToString("N"); @@ -32,11 +34,11 @@ namespace Volo.Abp.BlobStoring.Aliyun })); var configuration = context.Services.GetConfiguration(); - var _accessKeyId = configuration["Aliyun:AccessKeyId"]; - var _accessKeySecret = configuration["Aliyun:AccessKeySecret"]; - var _endpoint = configuration["Aliyun:Endpoint"]; - var _regionId = configuration["Aliyun:RegionId"]; - var _roleArn = configuration["Aliyun:RoleArn"]; + var accessKeyId = configuration["Aliyun:AccessKeyId"]; + var accessKeySecret = configuration["Aliyun:AccessKeySecret"]; + var endpoint = configuration["Aliyun:Endpoint"]; + var regionId = configuration["Aliyun:RegionId"]; + var roleArn = configuration["Aliyun:RoleArn"]; Configure(options => { @@ -44,14 +46,14 @@ namespace Volo.Abp.BlobStoring.Aliyun { containerConfiguration.UseAliyun(aliyun => { - aliyun.AccessKeyId = _accessKeyId; - aliyun.AccessKeySecret = _accessKeySecret; - aliyun.Endpoint = _endpoint; + aliyun.AccessKeyId = accessKeyId; + aliyun.AccessKeySecret = accessKeySecret; + aliyun.Endpoint = endpoint; //STS - aliyun.RegionId = _regionId; - aliyun.RoleArn = _roleArn; + aliyun.RegionId = regionId; + aliyun.RoleArn = roleArn; aliyun.RoleSessionName = Guid.NewGuid().ToString("N"); - aliyun.DurationSeconds = 3600; + aliyun.DurationSeconds = 0; aliyun.Policy = String.Empty; //Other aliyun.CreateContainerIfNotExists = true; @@ -66,7 +68,16 @@ namespace Volo.Abp.BlobStoring.Aliyun { var ossClientFactory = context.ServiceProvider.GetService(); var ossClient = ossClientFactory.Create(_configuration); - ossClient.DeleteBucket(_randomContainerName); + if(ossClient.DoesBucketExist(_randomContainerName)) + { + var objects = ossClient.ListObjects(_randomContainerName); + if (objects.ObjectSummaries.Any()) + { + ossClient.DeleteObjects(new DeleteObjectsRequest(_randomContainerName, + objects.ObjectSummaries.Select(o => o.Key).ToList())); + } + ossClient.DeleteBucket(_randomContainerName); + } } } From c08505ac32f09c18b7a2d70f8ef2d65863c941c9 Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Thu, 16 Jul 2020 13:29:26 +0800 Subject: [PATCH 07/10] chore: Add Volo.Abp.BlobStoring.Aliyun.csproj --- framework/Volo.Abp.sln | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 63162ef88b..5a83ae1460 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -315,6 +315,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Caching.StackExcha EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Caching.StackExchangeRedis.Tests", "test\Volo.Abp.Caching.StackExchangeRedis.Tests\Volo.Abp.Caching.StackExchangeRedis.Tests.csproj", "{60D0E384-965E-4F81-9D71-B28F419254FC}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Aliyun", "src\Volo.Abp.BlobStoring.Aliyun\Volo.Abp.BlobStoring.Aliyun.csproj", "{845E6A13-D1B5-4DDC-A16C-68D807E3B4C7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Aliyun.Tests", "test\Volo.Abp.BlobStoring.Aliyun.Tests\Volo.Abp.BlobStoring.Aliyun.Tests.csproj", "{8E49687A-E69F-49F2-8DB0-428D0883A937}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -937,6 +941,14 @@ Global {60D0E384-965E-4F81-9D71-B28F419254FC}.Debug|Any CPU.Build.0 = Debug|Any CPU {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {60D0E384-965E-4F81-9D71-B28F419254FC}.Release|Any CPU.Build.0 = Release|Any CPU + {845E6A13-D1B5-4DDC-A16C-68D807E3B4C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {845E6A13-D1B5-4DDC-A16C-68D807E3B4C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {845E6A13-D1B5-4DDC-A16C-68D807E3B4C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {845E6A13-D1B5-4DDC-A16C-68D807E3B4C7}.Release|Any CPU.Build.0 = Release|Any CPU + {8E49687A-E69F-49F2-8DB0-428D0883A937}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8E49687A-E69F-49F2-8DB0-428D0883A937}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8E49687A-E69F-49F2-8DB0-428D0883A937}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8E49687A-E69F-49F2-8DB0-428D0883A937}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1096,6 +1108,8 @@ Global {1738845A-5348-4EB8-B736-CD1D22A808B4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {60D0E384-965E-4F81-9D71-B28F419254FC} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {845E6A13-D1B5-4DDC-A16C-68D807E3B4C7} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {8E49687A-E69F-49F2-8DB0-428D0883A937} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} From 0ca750d257ec05adcdc58619c7847a83657fc2ff Mon Sep 17 00:00:00 2001 From: shaoxiaoxu Date: Thu, 16 Jul 2020 14:19:13 +0800 Subject: [PATCH 08/10] feat: Add Blob-Storing-Aliyun.md --- docs/zh-Hans/Blob-Storing-Aliyun.md | 72 +++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/zh-Hans/Blob-Storing-Aliyun.md diff --git a/docs/zh-Hans/Blob-Storing-Aliyun.md b/docs/zh-Hans/Blob-Storing-Aliyun.md new file mode 100644 index 0000000000..58b372f5a8 --- /dev/null +++ b/docs/zh-Hans/Blob-Storing-Aliyun.md @@ -0,0 +1,72 @@ +# BLOB Storing Aliyun提供程序 + +BLOB存储Aliyun提供程序可以将BLOB存储在[Aliyun Blob storage](https://help.aliyun.com/product/31815.html)中. + +> 阅读[BLOB存储文档](Blob-Storing.md)了解如何使用BLOB存储系统. 本文档仅介绍如何为容器配置Aliyun提供程序. + +## 安装 + +使用ABP CLI添加[Volo.Abp.BlobStoring.Aliyun](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Aliyun)NuGet包到你的项目: + +* 安装 [ABP CLI](https://docs.abp.io/en/abp/latest/CLI), 如果你还没有安装. +* 在要添加 `Volo.Abp.BlobStoring.Aliyun` 包的 `.csproj` 文件目录打开命令行. +* 运行 `Volo.Abp.BlobStoring.Aliyun` 命令. + +如果要手动安装,在你的项目中安装 `Volo.Abp.BlobStoring.Aliyun` NuGet包然后将`[DependsOn(typeof(AbpBlobStoringAliyunModule))]`添加到项目内的[ABP模块](Module-Development-Basics.md)类中. + +## 配置 + +如同[BLOB存储文档](Blob-Storing.md)所述,配置是在[模块](Module-Development-Basics.md)类的 `ConfigureServices` 方法完成的. + +**示例: 配置为默认使用Aliyun存储提供程序** + +````csharp +Configure(options => +{ + options.Containerscontainer.UseAliyun(aliyun => + { + aliyun.AccessKeyId = "your aliyun access key id"; + aliyun.AccessKeySecret = "your aliyun access key secret"; + aliyun.Endpoint = "your oss endpoint"; + aliyun.RegionId = "your sts region id"; + aliyun.RoleArn = "the arn of ram role"; + aliyun.RoleSessionName = "the name of the certificate"; + aliyun.Policy = "policy"; + aliyun.DurationSeconds = "expiration date"; + aliyun.ContainerName = "your aliyun container name"; + aliyun.CreateContainerIfNotExists = false; + }); +}); +```` + +> 参阅[BLOB存储文档](Blob-Storing.md) 学习如何为指定容器配置提供程序. + +### 选项 + +* **AccessKeyId** ([NotNull]string): 云账号AccessKey是访问阿里云API的密钥,具有该账户完全的权限,请您务必妥善保管!强烈建议遵循[阿里云安全最佳实践](https://help.aliyun.com/document_detail/102600.html) ,使用RAM子用户AccessKey来进行API调用。 +* **AccessKeySecret** ([NotNull]string): 同上。 +* **Endpoint** ([NotNull]string): Endpoint表示OSS对外服务的访问域名。[访问域名和数据中心](https://help.aliyun.com/document_detail/31837.html) +* **RegionId** (string): STS服务的接入地址,每个地址的功能都相同,请尽量在同地域进行调用。[接入地址](https://help.aliyun.com/document_detail/66053.html) +* **RoleArn** ([NotNull]string): STS所需角色ARN。[STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) +* **RoleSessionName** ([NotNull]string): 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分。 +* **Policy** (string): 在扮演角色的时候额外添加的权限限制。请参见[基于RAM Policy的权限控制](https://help.aliyun.com/document_detail/100680.html)。 +* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600。 **注**:为空则使用子账号操作OSS。 +* **ContainerName** (string): 你可以在aliyun中指定容器名称. 如果没有指定它将使用 `BlogContainerName` 属性定义的BLOB容器的名称(请参阅[BLOB存储文档](Blob-Storing.md)). 请注意Aliyun有一些**命名容器的规则**,容器名称必须是有效的DNS名称,[符合以下命名规则](https://help.aliyun.com/knowledge_detail/39668.html): + * 只能包含小写字母、数字和短横线(-) + * 必须以小写字母和数字开头和结尾 + * Bucket名称的长度限制在3-63个字符之间 +* **CreateContainerIfNotExists** (bool): 默认值为 `false`, 如果aliyun中不存在容器, `AliyunBlobProvider` 将尝试创建它. + +## Aliyun BLOB 名称计算器 + +Aliyun BLOB提供程序组织BLOB名称并实现一些约定. 默认情况下BLOB的全名由以下规则确定: + +* 如果当前租户为 `null`(或容器禁用多租户 - 请参阅[BLOB存储文档](Blob-Storing.md) 了解如何禁用容器的多租户),则追加 `host` 字符串. +* 如果当前租户不为 `null`,则追加 `tenants/` 字符串. +* 追加 BLOB 名称. + +## 其他服务 + +* `AliyunBlobProvider` 是实现Aliyun BLOB存储提供程序的主要服务,如果你想要通过[依赖注入](Dependency-Injection.md)覆盖/替换它(不要替换 `IBlobProvider` 接口,而是替换 `AliyunBlobProvider` 类). +* `IAliyunBlobNameCalculator` 服务用于计算文件路径. 默认实现是 `DefaultAliyunBlobNameCalculator` . 如果你想自定义文件路径计算,可以替换/覆盖它. +* `IOssClientFactory` 服务用于生成OSS客户端. 默认实现是 `DefaultOssClientFactory` . 如果你想自定义OSS客户端生成,可以替换/覆盖它. From 85c1d23389d6d0820e0daed91f5ce73bfcb0ddb1 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 16 Jul 2020 16:20:17 +0800 Subject: [PATCH 09/10] Update Blob-Storing-Aliyun.md --- docs/zh-Hans/Blob-Storing-Aliyun.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/zh-Hans/Blob-Storing-Aliyun.md b/docs/zh-Hans/Blob-Storing-Aliyun.md index 58b372f5a8..d9b13921a9 100644 --- a/docs/zh-Hans/Blob-Storing-Aliyun.md +++ b/docs/zh-Hans/Blob-Storing-Aliyun.md @@ -43,14 +43,14 @@ Configure(options => ### 选项 -* **AccessKeyId** ([NotNull]string): 云账号AccessKey是访问阿里云API的密钥,具有该账户完全的权限,请您务必妥善保管!强烈建议遵循[阿里云安全最佳实践](https://help.aliyun.com/document_detail/102600.html) ,使用RAM子用户AccessKey来进行API调用。 -* **AccessKeySecret** ([NotNull]string): 同上。 -* **Endpoint** ([NotNull]string): Endpoint表示OSS对外服务的访问域名。[访问域名和数据中心](https://help.aliyun.com/document_detail/31837.html) -* **RegionId** (string): STS服务的接入地址,每个地址的功能都相同,请尽量在同地域进行调用。[接入地址](https://help.aliyun.com/document_detail/66053.html) -* **RoleArn** ([NotNull]string): STS所需角色ARN。[STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) -* **RoleSessionName** ([NotNull]string): 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分。 -* **Policy** (string): 在扮演角色的时候额外添加的权限限制。请参见[基于RAM Policy的权限控制](https://help.aliyun.com/document_detail/100680.html)。 -* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600。 **注**:为空则使用子账号操作OSS。 +* **AccessKeyId** ([NotNull]string): 云账号AccessKey是访问阿里云API的密钥,具有该账户完全的权限,请你务必妥善保管!强烈建议遵循[阿里云安全最佳实践](https://help.aliyun.com/document_detail/102600.html),使用RAM子用户AccessKey来进行API调用. +* **AccessKeySecret** ([NotNull]string): 同上. +* **Endpoint** ([NotNull]string): Endpoint表示OSS对外服务的访问域名. [访问域名和数据中心](https://help.aliyun.com/document_detail/31837.html) +* **RegionId** (string): STS服务的接入地址,每个地址的功能都相同,请尽量在同地域进行调用. [接入地址](https://help.aliyun.com/document_detail/66053.html) +* **RoleArn** ([NotNull]string): STS所需角色ARN. [STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) +* **RoleSessionName** ([NotNull]string): 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分. +* **Policy** (string): 在扮演角色的时候额外添加的权限限制. 请参见[基于RAM Policy的权限控制](https://help.aliyun.com/document_detail/100680.html). +* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600. **注**:为空则使用子账号操作OSS. * **ContainerName** (string): 你可以在aliyun中指定容器名称. 如果没有指定它将使用 `BlogContainerName` 属性定义的BLOB容器的名称(请参阅[BLOB存储文档](Blob-Storing.md)). 请注意Aliyun有一些**命名容器的规则**,容器名称必须是有效的DNS名称,[符合以下命名规则](https://help.aliyun.com/knowledge_detail/39668.html): * 只能包含小写字母、数字和短横线(-) * 必须以小写字母和数字开头和结尾 @@ -68,5 +68,5 @@ Aliyun BLOB提供程序组织BLOB名称并实现一些约定. 默认情况下BLO ## 其他服务 * `AliyunBlobProvider` 是实现Aliyun BLOB存储提供程序的主要服务,如果你想要通过[依赖注入](Dependency-Injection.md)覆盖/替换它(不要替换 `IBlobProvider` 接口,而是替换 `AliyunBlobProvider` 类). -* `IAliyunBlobNameCalculator` 服务用于计算文件路径. 默认实现是 `DefaultAliyunBlobNameCalculator` . 如果你想自定义文件路径计算,可以替换/覆盖它. +* `IAliyunBlobNameCalculator` 服务用于计算文件路径. 默认实现是 `DefaultAliyunBlobNameCalculator`. 如果你想自定义文件路径计算,可以替换/覆盖它. * `IOssClientFactory` 服务用于生成OSS客户端. 默认实现是 `DefaultOssClientFactory` . 如果你想自定义OSS客户端生成,可以替换/覆盖它. From c444dd551d5d51496c319f71d0d5ba35011a8fa7 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Thu, 16 Jul 2020 23:03:30 +0800 Subject: [PATCH 10/10] Add aliyun blob provider document --- docs/en/Blob-Storing-Aliyun.md | 73 +++++++++++++++++++++++++++++ docs/en/docs-nav.json | 4 ++ docs/zh-Hans/Blob-Storing-Aliyun.md | 6 +-- docs/zh-Hans/docs-nav.json | 4 ++ 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 docs/en/Blob-Storing-Aliyun.md diff --git a/docs/en/Blob-Storing-Aliyun.md b/docs/en/Blob-Storing-Aliyun.md new file mode 100644 index 0000000000..6621e2dbd2 --- /dev/null +++ b/docs/en/Blob-Storing-Aliyun.md @@ -0,0 +1,73 @@ +# BLOB Storing Aliyun Provider + +BLOB Storing Aliyun Provider can store BLOBs in [Aliyun Blob storage](https://help.aliyun.com/product/31815.html). + +> Read the [BLOB Storing document](Blob-Storing.md) to understand how to use the BLOB storing system. This document only covers how to configure containers to use a Aliyun BLOB as the storage provider. + +## Installation + +Use the ABP CLI to add [Volo.Abp.BlobStoring.Aliyun](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Aliyun) NuGet package to your project: + +* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before. +* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.BlobStoring.Aliyun` package. +* Run `abp add-package Volo.Abp.BlobStoring.Aliyun` command. + +If you want to do it manually, install the [Volo.Abp.BlobStoring.Aliyun](https://www.nuget.org/packages/Volo.Abp.BlobStoring.Aliyun) NuGet package to your project and add `[DependsOn(typeof(AbpBlobStoringAliyunModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project. + +## Configuration + +Configuration is done in the `ConfigureServices` method of your [module](Module-Development-Basics.md) class, as explained in the [BLOB Storing document](Blob-Storing.md). + +**Example: Configure to use the Aliyun storage provider by default** + +````csharp +Configure(options => +{ + options.Containerscontainer.UseAliyun(aliyun => + { + aliyun.AccessKeyId = "your aliyun access key id"; + aliyun.AccessKeySecret = "your aliyun access key secret"; + aliyun.Endpoint = "your oss endpoint"; + aliyun.RegionId = "your sts region id"; + aliyun.RoleArn = "the arn of ram role"; + aliyun.RoleSessionName = "the name of the certificate"; + aliyun.Policy = "policy"; + aliyun.DurationSeconds = "expiration date"; + aliyun.ContainerName = "your aliyun container name"; + aliyun.CreateContainerIfNotExists = false; + }); +}); +```` + +> See the [BLOB Storing document](Blob-Storing.md) to learn how to configure this provider for a specific container. + +### Options + +* **AccessKeyId** ([NotNull]string): AccessKey is the key to access the Alibaba Cloud API. It has full permissions for the account. Please keep it safe! Recommend to follow [Alibaba Cloud security best practicess](https://help.aliyun.com/document_detail/102600.html),Use RAM sub-user AccessKey to call API. +* **AccessKeySecret** ([NotNull]string): Same as above. +* **Endpoint** ([NotNull]string): Endpoint is the external domain name of OSS. See the [document](https://help.aliyun.com/document_detail/31837.html) for details. +* **RegionId** (string): Access address of STS service. See the [document](https://help.aliyun.com/document_detail/66053.html) for details. +* **RoleArn** ([NotNull]string): STS required role ARN. See the [document](https://help.aliyun.com/document_detail/100624.html) for details. +* **RoleSessionName** ([NotNull]string): Used to identify the temporary access credentials, it is recommended to use different application users to distinguish. +* **Policy** (string): Additional permission restrictions. See the [document](https://help.aliyun.com/document_detail/100680.html) for details. +* **DurationSeconds** (int): Validity period(s) of a temporary access certificate,minimum is 900 and the maximum is 3600. **note**: Using subaccounts operated OSS,if the value is 0. +* **ContainerName** (string): You can specify the container name in Aliyun. If this is not specified, it uses the name of the BLOB container defined with the `BlogContainerName` attribute (see the [BLOB storing document](Blob-Storing.md)). Please note that Aliyun has some **rules for naming containers**. A container name must be a valid DNS name, conforming to the [following naming rules](https://help.aliyun.com/knowledge_detail/39668.html): + * Container names must start or end with a letter or number, and can contain only letters, numbers, and the dash (-) character. + * Container names Must start and end with lowercase letters and numbers. + * Container names must be from **3** through **63** characters long. +* **CreateContainerIfNotExists** (bool): Default value is `false`, If a container does not exist in Aliyun, `AliyunBlobProvider` will try to create it. + + +## Aliyun Blob Name Calculator + +Aliyun Blob Provider organizes BLOB name and implements some conventions. The full name of a BLOB is determined by the following rules by default: + +* Appends `host` string if [current tenant](Multi-Tenancy.md) is `null` (or multi-tenancy is disabled for the container - see the [BLOB Storing document](Blob-Storing.md) to learn how to disable multi-tenancy for a container). +* Appends `tenants/` string if current tenant is not `null`. +* Appends the BLOB name. + +## Other Services + +* `AliyunBlobProvider` is the main service that implements the Aliyun BLOB storage provider, if you want to override/replace it via [dependency injection](Dependency-Injection.md) (don't replace `IBlobProvider` interface, but replace `AliyunBlobProvider` class). +* `IAliyunBlobNameCalculator` is used to calculate the full BLOB name (that is explained above). It is implemented by the `DefaultAliyunBlobNameCalculator` by default. +* `IOssClientFactory` is used create OSS client. It is implemented by the `DefaultOssClientFactory` by default. You can override/replace it,if you want customize. \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index a6ab09dfee..eaeaad8346 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -216,6 +216,10 @@ "text": "Azure Provider", "path": "Blob-Storing-Azure.md" }, + { + "text": "Aliyun Provider", + "path": "Blob-Storing-Aliyun.md" + }, { "text": "Create a Custom Provider", "path": "Blob-Storing-Custom-Provider.md" diff --git a/docs/zh-Hans/Blob-Storing-Aliyun.md b/docs/zh-Hans/Blob-Storing-Aliyun.md index d9b13921a9..adeb902830 100644 --- a/docs/zh-Hans/Blob-Storing-Aliyun.md +++ b/docs/zh-Hans/Blob-Storing-Aliyun.md @@ -50,11 +50,11 @@ Configure(options => * **RoleArn** ([NotNull]string): STS所需角色ARN. [STS临时授权访问OSS](https://help.aliyun.com/document_detail/100624.html) * **RoleSessionName** ([NotNull]string): 用来标识临时访问凭证的名称,建议使用不同的应用程序用户来区分. * **Policy** (string): 在扮演角色的时候额外添加的权限限制. 请参见[基于RAM Policy的权限控制](https://help.aliyun.com/document_detail/100680.html). -* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600. **注**:为空则使用子账号操作OSS. +* **DurationSeconds** (int): 设置临时访问凭证的有效期,单位是s,最小为900,最大为3600. **注**:为0则使用子账号操作OSS. * **ContainerName** (string): 你可以在aliyun中指定容器名称. 如果没有指定它将使用 `BlogContainerName` 属性定义的BLOB容器的名称(请参阅[BLOB存储文档](Blob-Storing.md)). 请注意Aliyun有一些**命名容器的规则**,容器名称必须是有效的DNS名称,[符合以下命名规则](https://help.aliyun.com/knowledge_detail/39668.html): - * 只能包含小写字母、数字和短横线(-) + * 只能包含小写字母,数字和短横线(-) * 必须以小写字母和数字开头和结尾 - * Bucket名称的长度限制在3-63个字符之间 + * Bucket名称的长度限制在**3**到**63**个字符之间 * **CreateContainerIfNotExists** (bool): 默认值为 `false`, 如果aliyun中不存在容器, `AliyunBlobProvider` 将尝试创建它. ## Aliyun BLOB 名称计算器 diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 732acc8806..726ff8816c 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -210,6 +210,10 @@ "text": "Azure提供程序", "path": "Blob-Storing-Azure.md" }, + { + "text": "Aliyun提供程序", + "path": "Blob-Storing-Aliyun.md" + }, { "text": "创建自定义提供程序", "path": "Blob-Storing-Custom-Provider.md"