mirror of https://github.com/abpframework/abp.git
committed by
GitHub
26 changed files with 939 additions and 0 deletions
@ -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<AbpBlobStoringOptions>(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/<tenant-id>` 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. |
||||
@ -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<AbpBlobStoringOptions>(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. **注**:为0则使用子账号操作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/<tenant-id>` 字符串. |
||||
|
* 追加 BLOB 名称. |
||||
|
|
||||
|
## 其他服务 |
||||
|
|
||||
|
* `AliyunBlobProvider` 是实现Aliyun BLOB存储提供程序的主要服务,如果你想要通过[依赖注入](Dependency-Injection.md)覆盖/替换它(不要替换 `IBlobProvider` 接口,而是替换 `AliyunBlobProvider` 类). |
||||
|
* `IAliyunBlobNameCalculator` 服务用于计算文件路径. 默认实现是 `DefaultAliyunBlobNameCalculator`. 如果你想自定义文件路径计算,可以替换/覆盖它. |
||||
|
* `IOssClientFactory` 服务用于生成OSS客户端. 默认实现是 `DefaultOssClientFactory` . 如果你想自定义OSS客户端生成,可以替换/覆盖它. |
||||
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,27 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<AssemblyName>Volo.Abp.BlobStoring.Aliyun</AssemblyName> |
||||
|
<PackageId>Volo.Abp.BlobStoring.Aliyun</PackageId> |
||||
|
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="aliyun-net-sdk-sts" Version="3.0.4" /> |
||||
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.10.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" /> |
||||
|
<ProjectReference Include="..\Volo.Abp.Caching\Volo.Abp.Caching.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,14 @@ |
|||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpBlobStoringModule), |
||||
|
typeof(AbpCachingModule) |
||||
|
)] |
||||
|
public class AbpBlobStoringAliyunModule: AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -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<AliyunBlobProviderConfiguration> aliyunConfigureAction) |
||||
|
{ |
||||
|
containerConfiguration.ProviderType = typeof(AliyunBlobProvider); |
||||
|
containerConfiguration.NamingNormalizers.TryAdd<AliyunBlobNamingNormalizer>(); |
||||
|
|
||||
|
aliyunConfigureAction(new AliyunBlobProviderConfiguration(containerConfiguration)); |
||||
|
|
||||
|
return containerConfiguration; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
using System.Text.RegularExpressions; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
public class AliyunBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 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
|
||||
|
/// </summary>
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
return containerName; |
||||
|
} |
||||
|
|
||||
|
public virtual string NormalizeBlobName(string blobName) |
||||
|
{ |
||||
|
return blobName; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,107 @@ |
|||||
|
using Aliyun.OSS; |
||||
|
using System; |
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
protected virtual IOss GetOssClient(BlobContainerConfiguration blobContainerConfiguration) |
||||
|
{ |
||||
|
var aliyunConfig = blobContainerConfiguration.GetAliyunConfiguration(); |
||||
|
return OssClientFactory.Create(aliyunConfig); |
||||
|
} |
||||
|
|
||||
|
protected virtual IOss GetOssClient(AliyunBlobProviderConfiguration aliyunConfig) |
||||
|
{ |
||||
|
return OssClientFactory.Create(aliyunConfig); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public override Task SaveAsync(BlobProviderSaveArgs args) |
||||
|
{ |
||||
|
var containerName = GetContainerName(args); |
||||
|
var blobName = AliyunBlobNameCalculator.Calculate(args); |
||||
|
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 '{containerName}'! Set {nameof(args.OverrideExisting)} if it should be overwritten."); |
||||
|
} |
||||
|
if (aliyunConfig.CreateContainerIfNotExists) |
||||
|
{ |
||||
|
if (!ossClient.DoesBucketExist(containerName)) |
||||
|
{ |
||||
|
ossClient.CreateBucket(containerName); |
||||
|
} |
||||
|
} |
||||
|
ossClient.PutObject(containerName, blobName, args.BlobStream); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public override Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
||||
|
{ |
||||
|
var containerName = GetContainerName(args); |
||||
|
var blobName = AliyunBlobNameCalculator.Calculate(args); |
||||
|
var ossClient = GetOssClient(args.Configuration); |
||||
|
if(!BlobExistsAsync(ossClient, containerName, blobName)) |
||||
|
{ |
||||
|
return Task.FromResult(false); |
||||
|
} |
||||
|
ossClient.DeleteObject(containerName, blobName); |
||||
|
return Task.FromResult(true); |
||||
|
} |
||||
|
|
||||
|
public override Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
||||
|
{ |
||||
|
var containerName = GetContainerName(args); |
||||
|
var blobName = AliyunBlobNameCalculator.Calculate(args); |
||||
|
var ossClient = GetOssClient(args.Configuration); |
||||
|
return Task.FromResult(BlobExistsAsync(ossClient, containerName, blobName)); |
||||
|
} |
||||
|
|
||||
|
public override async Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
||||
|
{ |
||||
|
var containerName = GetContainerName(args); |
||||
|
var blobName = AliyunBlobNameCalculator.Calculate(args); |
||||
|
var ossClient = GetOssClient(args.Configuration); |
||||
|
if (!BlobExistsAsync(ossClient, containerName, blobName)) |
||||
|
{ |
||||
|
return null; |
||||
|
} |
||||
|
var result = ossClient.GetObject(containerName, blobName); |
||||
|
var memoryStream = new MemoryStream(); |
||||
|
await result.Content.CopyToAsync(memoryStream); |
||||
|
return memoryStream; |
||||
|
} |
||||
|
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Sub-account access to OSS or STS temporary authorization to access OSS
|
||||
|
/// </summary>
|
||||
|
public class AliyunBlobProviderConfiguration |
||||
|
{ |
||||
|
public string AccessKeyId |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.AccessKeyId); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.AccessKeyId, Check.NotNullOrWhiteSpace(value, nameof(value))); |
||||
|
} |
||||
|
|
||||
|
public string AccessKeySecret |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.AccessKeySecret); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.AccessKeySecret, Check.NotNullOrWhiteSpace(value, nameof(value))); |
||||
|
} |
||||
|
|
||||
|
public string Endpoint |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.Endpoint); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Endpoint, Check.NotNullOrWhiteSpace(value, nameof(value))); |
||||
|
} |
||||
|
|
||||
|
public string RegionId |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.RegionId); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RegionId, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// acs:ram::$accountID:role/$roleName
|
||||
|
/// </summary>
|
||||
|
public string RoleArn |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.RoleArn); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RoleArn, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The name used to identify the temporary access credentials, it is recommended to use different application users to distinguish.
|
||||
|
/// </summary>
|
||||
|
public string RoleSessionName |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.RoleSessionName); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.RoleSessionName, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Set the validity period of the temporary access credential, the unit is s, the minimum is 900, and the maximum is 3600.
|
||||
|
/// </summary>
|
||||
|
public int DurationSeconds |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfigurationOrDefault(AliyunBlobProviderConfigurationNames.DurationSeconds, 0); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.DurationSeconds, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// If policy is empty, the user will get all permissions under this role
|
||||
|
/// </summary>
|
||||
|
public string Policy |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.Policy); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.Policy, value); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 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 <see cref="BlobProviderArgs"/> will be used.
|
||||
|
/// </summary>
|
||||
|
public string ContainerName |
||||
|
{ |
||||
|
get => _containerConfiguration.GetConfiguration<string>(AliyunBlobProviderConfigurationNames.ContainerName); |
||||
|
set => _containerConfiguration.SetConfiguration(AliyunBlobProviderConfigurationNames.ContainerName, Check.NotNullOrWhiteSpace(value, nameof(value))); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Default value: false.
|
||||
|
/// </summary>
|
||||
|
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 ToKeyString() |
||||
|
{ |
||||
|
Uri uPoint = new Uri(Endpoint); |
||||
|
return $"blobstoring:aliyun:id:{AccessKeyId},sec:{AccessKeySecret},ept:{uPoint.Host.ToLower()},rid:{RegionId},ra:{RoleArn},rsn:{RoleSessionName},pl:{Policy}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
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 ContainerName = "Aliyun:ContainerName"; |
||||
|
public const string CreateContainerIfNotExists = "Aliyun.CreateContainerIfNotExists"; |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
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.Distributed; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Sub-account access to OSS or STS temporary authorization to access OSS
|
||||
|
/// </summary>
|
||||
|
public class DefaultOssClientFactory : IOssClientFactory, ITransientDependency |
||||
|
{ |
||||
|
protected IDistributedCache<AssumeRoleCredentialsCacheItem> Cache { get; } |
||||
|
public DefaultOssClientFactory( |
||||
|
IDistributedCache<AssumeRoleCredentialsCacheItem> cache) |
||||
|
{ |
||||
|
Cache = cache; |
||||
|
} |
||||
|
|
||||
|
public virtual IOss Create(AliyunBlobProviderConfiguration aliyunConfig) |
||||
|
{ |
||||
|
//Sub-account
|
||||
|
if (aliyunConfig.DurationSeconds <= 0) |
||||
|
{ |
||||
|
return new OssClient(aliyunConfig.Endpoint, aliyunConfig.AccessKeyId, aliyunConfig.AccessKeySecret); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
//STS temporary authorization to access OSS
|
||||
|
var key = aliyunConfig.ToKeyString(); |
||||
|
var cacheItem = Cache.Get(key); |
||||
|
if (cacheItem == null) |
||||
|
{ |
||||
|
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); |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
public interface IAliyunBlobNameCalculator |
||||
|
{ |
||||
|
string Calculate(BlobProviderArgs args); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Aliyun.OSS; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
public interface IOssClientFactory |
||||
|
{ |
||||
|
IOss Create(AliyunBlobProviderConfiguration args); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.test.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp3.1</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
<UserSecretsId>9f0d2c00-80c1-435b-bfab-2c39c8249091</UserSecretsId> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\src\Volo.Abp.BlobStoring.Aliyun\Volo.Abp.BlobStoring.Aliyun.csproj" /> |
||||
|
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
||||
|
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="3.1.5" /> |
||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> |
||||
|
<ProjectReference Include="..\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<Folder Include="Properties\" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,19 @@ |
|||||
|
using Volo.Abp.Testing; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
public class AbpBlobStoringAliyunTestCommonBase : AbpIntegratedTest<AbpBlobStoringAliyunTestCommonModule> |
||||
|
{ |
||||
|
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
||||
|
{ |
||||
|
options.UseAutofac(); |
||||
|
} |
||||
|
} |
||||
|
public class AbpBlobStoringAliyunTestBase : AbpIntegratedTest<AbpBlobStoringAliyunTestModule> |
||||
|
{ |
||||
|
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
||||
|
{ |
||||
|
options.UseAutofac(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
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 |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpBlobStoringAliyunModule), |
||||
|
typeof(AbpBlobStoringTestModule) |
||||
|
)] |
||||
|
public class AbpBlobStoringAliyunTestCommonModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpBlobStoringAliyunTestCommonModule) |
||||
|
)] |
||||
|
public class AbpBlobStoringAliyunTestModule : AbpModule |
||||
|
{ |
||||
|
private const string UserSecretsId = "9f0d2c00-80c1-435b-bfab-2c39c8249091"; |
||||
|
|
||||
|
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 => |
||||
|
{ |
||||
|
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<AbpBlobStoringOptions>(options => |
||||
|
{ |
||||
|
options.Containers.ConfigureAll((containerName, containerConfiguration) => |
||||
|
{ |
||||
|
containerConfiguration.UseAliyun(aliyun => |
||||
|
{ |
||||
|
aliyun.AccessKeyId = accessKeyId; |
||||
|
aliyun.AccessKeySecret = accessKeySecret; |
||||
|
aliyun.Endpoint = endpoint; |
||||
|
//STS
|
||||
|
aliyun.RegionId = regionId; |
||||
|
aliyun.RoleArn = roleArn; |
||||
|
aliyun.RoleSessionName = Guid.NewGuid().ToString("N"); |
||||
|
aliyun.DurationSeconds = 0; |
||||
|
aliyun.Policy = String.Empty; |
||||
|
//Other
|
||||
|
aliyun.CreateContainerIfNotExists = true; |
||||
|
aliyun.ContainerName = _randomContainerName; |
||||
|
_configuration = aliyun; |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void OnApplicationShutdown(ApplicationShutdownContext context) |
||||
|
{ |
||||
|
var ossClientFactory = context.ServiceProvider.GetService<IOssClientFactory>(); |
||||
|
var ossClient = ossClientFactory.Create(_configuration); |
||||
|
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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
using Shouldly; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
/* |
||||
|
//Please set the correct connection string in secrets.json and continue the test.
|
||||
|
public class AliyunBlobContainer_Tests : BlobContainer_Tests<AbpBlobStoringAliyunTestModule> |
||||
|
{ |
||||
|
public AliyunBlobContainer_Tests() |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
*/ |
||||
|
} |
||||
@ -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<IAliyunBlobNameCalculator>(); |
||||
|
_currentTenant = GetRequiredService<ICurrentTenant>(); |
||||
|
} |
||||
|
|
||||
|
[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 |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Aliyun |
||||
|
{ |
||||
|
public class DefaultAliyunBlobNamingNormalizerProvider_Tests: AbpBlobStoringAliyunTestCommonBase |
||||
|
{ |
||||
|
private readonly IBlobNamingNormalizer _blobNamingNormalizer; |
||||
|
|
||||
|
public DefaultAliyunBlobNamingNormalizerProvider_Tests() |
||||
|
{ |
||||
|
_blobNamingNormalizer = GetRequiredService<IBlobNamingNormalizer>(); |
||||
|
} |
||||
|
|
||||
|
[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); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue