mirror of https://github.com/abpframework/abp.git
413 changed files with 11011 additions and 5871 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,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,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.BlobStoring.Minio</AssemblyName> |
|||
<PackageId>Volo.Abp.BlobStoring.Minio</PackageId> |
|||
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" /> |
|||
<PackageReference Include="Minio" Version="3.1.13" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,10 @@ |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
[DependsOn(typeof(AbpBlobStoringModule))] |
|||
public class AbpBlobStoringMinioModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public class DefaultMinioBlobNameCalculator : IMinioBlobNameCalculator, ITransientDependency |
|||
{ |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
|
|||
public DefaultMinioBlobNameCalculator(ICurrentTenant currentTenant) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
} |
|||
|
|||
public virtual string Calculate(BlobProviderArgs args) |
|||
{ |
|||
return CurrentTenant.Id == null |
|||
? $"host/{args.BlobName}" |
|||
: $"tenants/{CurrentTenant.Id.Value.ToString("D")}/{args.BlobName}"; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public interface IMinioBlobNameCalculator |
|||
{ |
|||
string Calculate(BlobProviderArgs args); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public static class MinioBlobContainerConfigurationExtensions |
|||
{ |
|||
public static MinioBlobProviderConfiguration GetMinioConfiguration( |
|||
this BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
return new MinioBlobProviderConfiguration(containerConfiguration); |
|||
} |
|||
|
|||
public static BlobContainerConfiguration UseMinio( |
|||
this BlobContainerConfiguration containerConfiguration, |
|||
Action<MinioBlobProviderConfiguration> minioConfigureAction) |
|||
{ |
|||
containerConfiguration.ProviderType = typeof(MinioBlobProvider); |
|||
|
|||
minioConfigureAction(new MinioBlobProviderConfiguration(containerConfiguration)); |
|||
|
|||
return containerConfiguration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
using System.Text.RegularExpressions; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public class MinioBlobNamingNormalizer : IBlobNamingNormalizer, ITransientDependency |
|||
{ |
|||
/// <summary>
|
|||
///https://docs.aws.amazon.com/AmazonS3/latest/dev/BucketRestrictions.html
|
|||
/// </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; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html
|
|||
/// </summary>
|
|||
public virtual string NormalizeBlobName(string blobName) |
|||
{ |
|||
return blobName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,145 @@ |
|||
using Minio; |
|||
using Minio.Exceptions; |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public class MinioBlobProvider : BlobProviderBase, ITransientDependency |
|||
{ |
|||
protected IMinioBlobNameCalculator MinioBlobNameCalculator { get; } |
|||
|
|||
public MinioBlobProvider(IMinioBlobNameCalculator minioBlobNameCalculator) |
|||
{ |
|||
MinioBlobNameCalculator = minioBlobNameCalculator; |
|||
} |
|||
|
|||
public override async Task SaveAsync(BlobProviderSaveArgs args) |
|||
{ |
|||
var blobName = MinioBlobNameCalculator.Calculate(args); |
|||
var configuration = args.Configuration.GetMinioConfiguration(); |
|||
var client = GetMinioClient(args); |
|||
var containerName = GetContainerName(args); |
|||
|
|||
if (!args.OverrideExisting && await BlobExistsAsync(client, 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 (configuration.CreateBucketIfNotExists) |
|||
{ |
|||
await CreateBucketIfNotExists(client, containerName); |
|||
} |
|||
|
|||
await client.PutObjectAsync(containerName, blobName, args.BlobStream, args.BlobStream.Length); |
|||
} |
|||
|
|||
public override async Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
|||
{ |
|||
var blobName = MinioBlobNameCalculator.Calculate(args); |
|||
var client = GetMinioClient(args); |
|||
var containerName = GetContainerName(args); |
|||
|
|||
if (await BlobExistsAsync(client, containerName, blobName)) |
|||
{ |
|||
await client.RemoveObjectAsync(containerName, blobName); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public override async Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
|||
{ |
|||
var blobName = MinioBlobNameCalculator.Calculate(args); |
|||
var client = GetMinioClient(args); |
|||
var containerName = GetContainerName(args); |
|||
|
|||
return await BlobExistsAsync(client, containerName, blobName); |
|||
} |
|||
|
|||
public override async Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
|||
{ |
|||
var blobName = MinioBlobNameCalculator.Calculate(args); |
|||
var client = GetMinioClient(args); |
|||
var containerName = GetContainerName(args); |
|||
|
|||
if (!await BlobExistsAsync(client, containerName, blobName)) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
var memoryStream = new MemoryStream(); |
|||
await client.GetObjectAsync(containerName, blobName, stream => |
|||
{ |
|||
if (stream != null) |
|||
{ |
|||
stream.CopyTo(memoryStream); |
|||
} |
|||
else |
|||
{ |
|||
memoryStream = null; |
|||
} |
|||
}); |
|||
|
|||
return memoryStream; |
|||
} |
|||
|
|||
protected virtual MinioClient GetMinioClient(BlobProviderArgs args) |
|||
{ |
|||
var configuration = args.Configuration.GetMinioConfiguration(); |
|||
var client = new MinioClient(configuration.EndPoint, configuration.AccessKey, configuration.SecretKey); |
|||
|
|||
if (configuration.WithSSL) |
|||
{ |
|||
client.WithSSL(); |
|||
} |
|||
|
|||
return client; |
|||
} |
|||
|
|||
protected virtual async Task CreateBucketIfNotExists(MinioClient client,string containerName) |
|||
{ |
|||
if (!await client.BucketExistsAsync(containerName)) |
|||
{ |
|||
await client.MakeBucketAsync(containerName); |
|||
} |
|||
} |
|||
|
|||
private async Task<bool> BlobExistsAsync(MinioClient client, string containerName , string blobName) |
|||
{ |
|||
// Make sure Blob Container exists.
|
|||
if (await client.BucketExistsAsync(containerName)) |
|||
{ |
|||
try |
|||
{ |
|||
await client.StatObjectAsync(containerName, blobName); |
|||
} |
|||
catch (Exception e) |
|||
{ |
|||
if (e is ObjectNotFoundException) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
throw; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private static string GetContainerName(BlobProviderArgs args) |
|||
{ |
|||
var configuration = args.Configuration.GetMinioConfiguration(); |
|||
|
|||
return configuration.BucketName.IsNullOrWhiteSpace() |
|||
? args.ContainerName |
|||
: configuration.BucketName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public class MinioBlobProviderConfiguration |
|||
{ |
|||
public string BucketName |
|||
{ |
|||
get => _containerConfiguration.GetConfiguration<string>(MinioBlobProviderConfigurationNames.BucketName); |
|||
set => _containerConfiguration.SetConfiguration(MinioBlobProviderConfigurationNames.BucketName, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// endPoint is an URL, domain name, IPv4 address or IPv6 address.
|
|||
/// </summary>
|
|||
public string EndPoint |
|||
{ |
|||
get => _containerConfiguration.GetConfiguration<string>(MinioBlobProviderConfigurationNames.EndPoint); |
|||
set => _containerConfiguration.SetConfiguration(MinioBlobProviderConfigurationNames.EndPoint, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// accessKey is like user-id that uniquely identifies your account.This field is optional and can be omitted for anonymous access.
|
|||
/// </summary>
|
|||
public string AccessKey |
|||
{ |
|||
get => _containerConfiguration.GetConfiguration<string>(MinioBlobProviderConfigurationNames.AccessKey); |
|||
set => _containerConfiguration.SetConfiguration(MinioBlobProviderConfigurationNames.AccessKey, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// secretKey is the password to your account.This field is optional and can be omitted for anonymous access.
|
|||
/// </summary>
|
|||
public string SecretKey |
|||
{ |
|||
get => _containerConfiguration.GetConfiguration<string>(MinioBlobProviderConfigurationNames.SecretKey); |
|||
set => _containerConfiguration.SetConfiguration(MinioBlobProviderConfigurationNames.SecretKey, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
///connect to to MinIO Client object to use https instead of http
|
|||
/// </summary>
|
|||
public bool WithSSL |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(MinioBlobProviderConfigurationNames.WithSSL, false); |
|||
set => _containerConfiguration.SetConfiguration(MinioBlobProviderConfigurationNames.WithSSL, value); |
|||
} |
|||
|
|||
/// <summary>
|
|||
///Default value: false.
|
|||
/// </summary>
|
|||
public bool CreateBucketIfNotExists |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(MinioBlobProviderConfigurationNames.CreateBucketIfNotExists, false); |
|||
set => _containerConfiguration.SetConfiguration(MinioBlobProviderConfigurationNames.CreateBucketIfNotExists, value); |
|||
} |
|||
|
|||
private readonly BlobContainerConfiguration _containerConfiguration; |
|||
|
|||
public MinioBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
_containerConfiguration = containerConfiguration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public static class MinioBlobProviderConfigurationNames |
|||
{ |
|||
public const string BucketName = "Minio.BucketName"; |
|||
public const string EndPoint = "Minio.EndPoint"; |
|||
public const string AccessKey = "Minio.AccessKey"; |
|||
public const string SecretKey = "Minio.SecretKey"; |
|||
public const string WithSSL = "Minio.WithSSL"; |
|||
public const string CreateBucketIfNotExists = "Minio.CreateBucketIfNotExists"; |
|||
} |
|||
} |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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); |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
<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.Minio\Volo.Abp.BlobStoring.Minio.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> |
|||
<ProjectReference Include="..\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,20 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public class AbpBlobStoringMinioTestCommonBase : AbpIntegratedTest<AbpBlobStoringMinioTestCommonModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
|
|||
public class AbpBlobStoringMinioTestBase : AbpIntegratedTest<AbpBlobStoringMinioTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reactive.Linq; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Minio; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
|
|||
[DependsOn( |
|||
typeof(AbpBlobStoringMinioModule), |
|||
typeof(AbpBlobStoringTestModule) |
|||
)] |
|||
public class AbpBlobStoringMinioTestCommonModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
|
|||
[DependsOn( |
|||
typeof(AbpBlobStoringMinioTestCommonModule) |
|||
)] |
|||
public class AbpBlobStoringMinioTestModule : AbpModule |
|||
{ |
|||
private const string UserSecretsId = "9f0d2c00-80c1-435b-bfab-2c39c8249091"; |
|||
|
|||
private string _endPoint; |
|||
private string _accessKey; |
|||
private string _secretKey; |
|||
|
|||
private readonly string _randomContainerName = "abp-minio-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(); |
|||
_endPoint = configuration["Minio:EndPoint"]; |
|||
_accessKey = configuration["Minio:AccessKey"]; |
|||
_secretKey = configuration["Minio:SecretKey"]; |
|||
|
|||
Configure<AbpBlobStoringOptions>(options => |
|||
{ |
|||
options.Containers.ConfigureAll((containerName, containerConfiguration) => |
|||
{ |
|||
containerConfiguration.UseMinio(minio => |
|||
{ |
|||
minio.EndPoint = _endPoint; |
|||
minio.AccessKey = _accessKey; |
|||
minio.SecretKey = _secretKey; |
|||
minio.WithSSL = false; |
|||
minio.BucketName = _randomContainerName; |
|||
minio.CreateBucketIfNotExists = true; |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
|
|||
public override async void OnApplicationShutdown(ApplicationShutdownContext context) |
|||
{ |
|||
var minioClient = new MinioClient(_endPoint, _accessKey, _secretKey); |
|||
if (await minioClient.BucketExistsAsync(_randomContainerName)) |
|||
{ |
|||
var objects = await minioClient.ListObjectsAsync(_randomContainerName, null, true).ToList(); |
|||
|
|||
foreach (var item in objects) |
|||
{ |
|||
await minioClient.RemoveObjectAsync(_randomContainerName, item.Key); |
|||
} |
|||
|
|||
await minioClient.RemoveBucketAsync(_randomContainerName); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,57 @@ |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public class DefaultMinioBlobNamingNormalizerProvider_Tests : AbpBlobStoringMinioTestCommonBase |
|||
{ |
|||
private readonly IBlobNamingNormalizer _blobNamingNormalizer; |
|||
|
|||
public DefaultMinioBlobNamingNormalizerProvider_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); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
/* |
|||
//Please set the correct connection string in secrets.json and continue the test.
|
|||
public class MinioBlobContainer_Tests : BlobContainer_Tests<AbpBlobStoringMinioTestModule> |
|||
{ |
|||
public MinioBlobContainer_Tests() |
|||
{ |
|||
|
|||
|
|||
} |
|||
} |
|||
*/ |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using System; |
|||
using Shouldly; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring.Minio |
|||
{ |
|||
public class MinioBlobNameCalculator_Tests : AbpBlobStoringMinioTestCommonBase |
|||
{ |
|||
private readonly IMinioBlobNameCalculator _calculator; |
|||
private readonly ICurrentTenant _currentTenant; |
|||
|
|||
private const string MinioSeparator = "/"; |
|||
|
|||
public MinioBlobNameCalculator_Tests() |
|||
{ |
|||
_calculator = GetRequiredService<IMinioBlobNameCalculator>(); |
|||
_currentTenant = GetRequiredService<ICurrentTenant>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Default_Settings() |
|||
{ |
|||
_calculator.Calculate( |
|||
GetArgs("my-container", "my-blob") |
|||
).ShouldBe($"host{MinioSeparator}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{MinioSeparator}{tenantId:D}{MinioSeparator}my-blob"); |
|||
} |
|||
} |
|||
|
|||
private static BlobProviderArgs GetArgs( |
|||
string containerName, |
|||
string blobName) |
|||
{ |
|||
return new BlobProviderGetArgs( |
|||
containerName, |
|||
new BlobContainerConfiguration().UseMinio(x => |
|||
{ |
|||
x.BucketName = containerName; |
|||
}), |
|||
blobName |
|||
); |
|||
} |
|||
} |
|||
} |
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue