Browse Source

Merge pull request #4688 from mhymhygi/miniostoring

Add Minio blob provider
pull/4776/head
liangshiwei 6 years ago
committed by GitHub
parent
commit
9d05992f07
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      framework/Volo.Abp.sln
  2. 3
      framework/src/Volo.Abp.BlobStoring.Minio/FodyWeavers.xml
  3. 30
      framework/src/Volo.Abp.BlobStoring.Minio/FodyWeavers.xsd
  4. 21
      framework/src/Volo.Abp.BlobStoring.Minio/Volo.Abp.BlobStoring.Minio.csproj
  5. 10
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/AbpBlobStoringMinioModule.cs
  6. 22
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/DefaultMinioBlobNameCalculator.cs
  7. 7
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/IMinioBlobNameCalculator.cs
  8. 24
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobContainerConfigurationExtensions.cs
  9. 52
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobNamingNormalizer.cs
  10. 145
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs
  11. 63
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProviderConfiguration.cs
  12. 12
      framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProviderConfigurationNames.cs
  13. 17
      framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo.Abp.BlobStoring.Minio.Tests.csproj
  14. 20
      framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/AbpBlobStoringMinioTestBase.cs
  15. 81
      framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/AbpBlobStoringMinioTestModule.cs
  16. 57
      framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/DefaultMinioBlobNamingNormalizerProvider_Tests.cs
  17. 16
      framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobContainer_Tests.cs
  18. 56
      framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobNameCalculator_Tests.cs
  19. 1
      nupkg/common.ps1

36
framework/Volo.Abp.sln

@ -289,27 +289,31 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR.Tests", "test\Volo.Abp.AspNetCore.SignalR.Tests\Volo.Abp.AspNetCore.SignalR.Tests.csproj", "{8B758716-DCC9-4223-8421-5588D1597487}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj", "{79323211-E658-493E-9863-035AA4C3F913}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj", "{79323211-E658-493E-9863-035AA4C3F913}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring", "src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj", "{A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring", "src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj", "{A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Tests", "test\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj", "{D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Tests", "test\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj", "{D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem", "src\Volo.Abp.BlobStoring.FileSystem\Volo.Abp.BlobStoring.FileSystem.csproj", "{02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.FileSystem", "src\Volo.Abp.BlobStoring.FileSystem\Volo.Abp.BlobStoring.FileSystem.csproj", "{02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem.Tests", "test\Volo.Abp.BlobStoring.FileSystem.Tests\Volo.Abp.BlobStoring.FileSystem.Tests.csproj", "{68443D4A-1608-4039-B995-7AF4CF82E9F8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.FileSystem.Tests", "test\Volo.Abp.BlobStoring.FileSystem.Tests\Volo.Abp.BlobStoring.FileSystem.Tests.csproj", "{68443D4A-1608-4039-B995-7AF4CF82E9F8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle.Devart", "src\Volo.Abp.EntityFrameworkCore.Oracle.Devart\Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj", "{75E5C841-5F36-4C44-A532-57CB8E7FFE15}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Oracle.Devart", "src\Volo.Abp.EntityFrameworkCore.Oracle.Devart\Volo.Abp.EntityFrameworkCore.Oracle.Devart.csproj", "{75E5C841-5F36-4C44-A532-57CB8E7FFE15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure", "src\Volo.Abp.BlobStoring.Azure\Volo.Abp.BlobStoring.Azure.csproj", "{C44242F7-D55D-4867-AAF4-A786E404312E}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Azure", "src\Volo.Abp.BlobStoring.Azure\Volo.Abp.BlobStoring.Azure.csproj", "{C44242F7-D55D-4867-AAF4-A786E404312E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Azure.Tests", "test\Volo.Abp.BlobStoring.Azure.Tests\Volo.Abp.BlobStoring.Azure.Tests.csproj", "{A80E9A0B-8932-4B5D-83FB-6751708FD484}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Azure.Tests", "test\Volo.Abp.BlobStoring.Azure.Tests\Volo.Abp.BlobStoring.Azure.Tests.csproj", "{A80E9A0B-8932-4B5D-83FB-6751708FD484}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EntityFrameworkCore.Oracle", "src\Volo.Abp.EntityFrameworkCore.Oracle\Volo.Abp.EntityFrameworkCore.Oracle.csproj", "{1738845A-5348-4EB8-B736-CD1D22A808B4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Minio", "src\Volo.Abp.BlobStoring.Minio\Volo.Abp.BlobStoring.Minio.csproj", "{658D7EDE-A057-4256-96B6-083D3C2B9704}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis", "src\Volo.Abp.Caching.StackExchangeRedis\Volo.Abp.Caching.StackExchangeRedis.csproj", "{2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.BlobStoring.Minio.Tests", "test\Volo.Abp.BlobStoring.Minio.Tests\Volo.Abp.BlobStoring.Minio.Tests.csproj", "{36D4B268-FD3A-4655-A41B-D56D68476C83}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching.StackExchangeRedis.Tests", "test\Volo.Abp.Caching.StackExchangeRedis.Tests\Volo.Abp.Caching.StackExchangeRedis.Tests.csproj", "{60D0E384-965E-4F81-9D71-B28F419254FC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.EntityFrameworkCore.Oracle", "src\Volo.Abp.EntityFrameworkCore.Oracle\Volo.Abp.EntityFrameworkCore.Oracle.csproj", "{1738845A-5348-4EB8-B736-CD1D22A808B4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Caching.StackExchangeRedis", "src\Volo.Abp.Caching.StackExchangeRedis\Volo.Abp.Caching.StackExchangeRedis.csproj", "{2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Caching.StackExchangeRedis.Tests", "test\Volo.Abp.Caching.StackExchangeRedis.Tests\Volo.Abp.Caching.StackExchangeRedis.Tests.csproj", "{60D0E384-965E-4F81-9D71-B28F419254FC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -913,6 +917,14 @@ Global
{A80E9A0B-8932-4B5D-83FB-6751708FD484}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A80E9A0B-8932-4B5D-83FB-6751708FD484}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A80E9A0B-8932-4B5D-83FB-6751708FD484}.Release|Any CPU.Build.0 = Release|Any CPU
{658D7EDE-A057-4256-96B6-083D3C2B9704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{658D7EDE-A057-4256-96B6-083D3C2B9704}.Debug|Any CPU.Build.0 = Debug|Any CPU
{658D7EDE-A057-4256-96B6-083D3C2B9704}.Release|Any CPU.ActiveCfg = Release|Any CPU
{658D7EDE-A057-4256-96B6-083D3C2B9704}.Release|Any CPU.Build.0 = Release|Any CPU
{36D4B268-FD3A-4655-A41B-D56D68476C83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{36D4B268-FD3A-4655-A41B-D56D68476C83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{36D4B268-FD3A-4655-A41B-D56D68476C83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{36D4B268-FD3A-4655-A41B-D56D68476C83}.Release|Any CPU.Build.0 = Release|Any CPU
{1738845A-5348-4EB8-B736-CD1D22A808B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1738845A-5348-4EB8-B736-CD1D22A808B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1738845A-5348-4EB8-B736-CD1D22A808B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -1079,6 +1091,8 @@ Global
{75E5C841-5F36-4C44-A532-57CB8E7FFE15} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{C44242F7-D55D-4867-AAF4-A786E404312E} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{A80E9A0B-8932-4B5D-83FB-6751708FD484} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{658D7EDE-A057-4256-96B6-083D3C2B9704} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{36D4B268-FD3A-4655-A41B-D56D68476C83} = {447C8A77-E5F0-4538-8687-7383196D04EA}
{1738845A-5348-4EB8-B736-CD1D22A808B4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{2B83DF1F-0FD2-4DEA-ABC5-E324B51401D4} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6}
{60D0E384-965E-4F81-9D71-B28F419254FC} = {447C8A77-E5F0-4538-8687-7383196D04EA}

3
framework/src/Volo.Abp.BlobStoring.Minio/FodyWeavers.xml

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

30
framework/src/Volo.Abp.BlobStoring.Minio/FodyWeavers.xsd

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

21
framework/src/Volo.Abp.BlobStoring.Minio/Volo.Abp.BlobStoring.Minio.csproj

@ -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>

10
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/AbpBlobStoringMinioModule.cs

@ -0,0 +1,10 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.BlobStoring.Minio
{
[DependsOn(typeof(AbpBlobStoringModule))]
public class AbpBlobStoringMinioModule : AbpModule
{
}
}

22
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/DefaultMinioBlobNameCalculator.cs

@ -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}";
}
}
}

7
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/IMinioBlobNameCalculator.cs

@ -0,0 +1,7 @@
namespace Volo.Abp.BlobStoring.Minio
{
public interface IMinioBlobNameCalculator
{
string Calculate(BlobProviderArgs args);
}
}

24
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobContainerConfigurationExtensions.cs

@ -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;
}
}
}

52
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobNamingNormalizer.cs

@ -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;
}
}
}

145
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProvider.cs

@ -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;
}
}
}

63
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProviderConfiguration.cs

@ -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;
}
}
}

12
framework/src/Volo.Abp.BlobStoring.Minio/Volo/Abp/BlobStoring/Minio/MinioBlobProviderConfigurationNames.cs

@ -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";
}
}

17
framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo.Abp.BlobStoring.Minio.Tests.csproj

@ -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>

20
framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/AbpBlobStoringMinioTestBase.cs

@ -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();
}
}
}

81
framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/AbpBlobStoringMinioTestModule.cs

@ -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);
}
}
}
}

57
framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/DefaultMinioBlobNamingNormalizerProvider_Tests.cs

@ -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);
}
}
}

16
framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobContainer_Tests.cs

@ -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()
{
}
}
*/
}

56
framework/test/Volo.Abp.BlobStoring.Minio.Tests/Volo/Abp/BlobStoring/Minio/MinioBlobNameCalculator_Tests.cs

@ -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
);
}
}
}

1
nupkg/common.ps1

@ -59,6 +59,7 @@ $projects = (
"framework/src/Volo.Abp.BlobStoring",
"framework/src/Volo.Abp.BlobStoring.FileSystem",
"framework/src/Volo.Abp.BlobStoring.Azure",
"framework/src/Volo.Abp.BlobStoring.Minio",
"framework/src/Volo.Abp.Caching",
"framework/src/Volo.Abp.Caching.StackExchangeRedis",
"framework/src/Volo.Abp.Castle.Core",

Loading…
Cancel
Save