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",