Browse Source

Add NamingNormalizerProviders to BlobContainerConfiguration.

Resolve #4408
pull/4471/head
maliming 6 years ago
parent
commit
f1f0bd3a97
  1. 1
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs
  2. 52
      framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider.cs
  3. 41
      framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider.cs
  4. 9
      framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs
  5. 64
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs
  6. 10
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs
  7. 24
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs
  8. 9
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizerProvider.cs
  9. 9
      framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs
  10. 29
      framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs
  11. 57
      framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs
  12. 62
      framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs

1
framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/AzureBlobContainerConfigurationExtensions.cs

@ -15,6 +15,7 @@ namespace Volo.Abp.BlobStoring.Azure
Action<AzureBlobProviderConfiguration> azureConfigureAction)
{
containerConfiguration.ProviderType = typeof(AzureBlobProvider);
containerConfiguration.NamingNormalizerProviders.Add<DefaultAzureBlobNamingNormalizerProvider>();
azureConfigureAction(new AzureBlobProviderConfiguration(containerConfiguration));

52
framework/src/Volo.Abp.BlobStoring.Azure/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider.cs

@ -0,0 +1,52 @@
using System.Text.RegularExpressions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.BlobStoring.Azure
{
public class DefaultAzureBlobNamingNormalizerProvider : IBlobNamingNormalizerProvider, ITransientDependency
{
/// <summary>
///https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
/// </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.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#blob-names
/// </summary>
public virtual string NormalizeBlobName(string blobName)
{
return blobName;
}
}
}

41
framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider.cs

@ -0,0 +1,41 @@
using System;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.BlobStoring.FileSystem
{
public class DefaultFileSystemBlobNamingNormalizerProvider : IBlobNamingNormalizerProvider, ITransientDependency
{
private readonly IOSPlatformProvider _iosPlatformProvider;
public DefaultFileSystemBlobNamingNormalizerProvider(IOSPlatformProvider iosPlatformProvider)
{
_iosPlatformProvider = iosPlatformProvider;
}
public virtual string NormalizeContainerName(string containerName)
{
return Normalize(containerName);
}
public virtual string NormalizeBlobName(string blobName)
{
return Normalize(blobName);
}
protected virtual string Normalize(string fileName)
{
var os = _iosPlatformProvider.GetCurrentOSPlatform();
if (os == OSPlatform.Windows)
{
// A filename cannot contain any of the following characters: \ / : * ? " < > |
// In order to support the directory included in the blob name, remove / and \
fileName = Regex.Replace(fileName, "[:\\*\\?\"<>\\|]", string.Empty);
}
return fileName;
}
}
}

9
framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs

@ -9,16 +9,17 @@ namespace Volo.Abp.BlobStoring.FileSystem
{
return new FileSystemBlobProviderConfiguration(containerConfiguration);
}
public static BlobContainerConfiguration UseFileSystem(
this BlobContainerConfiguration containerConfiguration,
Action<FileSystemBlobProviderConfiguration> fileSystemConfigureAction)
{
containerConfiguration.ProviderType = typeof(FileSystemBlobProvider);
containerConfiguration.NamingNormalizerProviders.Add<DefaultFileSystemBlobNamingNormalizerProvider>();
fileSystemConfigureAction(new FileSystemBlobProviderConfiguration(containerConfiguration));
return containerConfiguration;
}
}
}
}

64
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs

@ -1,7 +1,9 @@
using System;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
@ -84,18 +86,44 @@ namespace Volo.Abp.BlobStoring
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IServiceProvider ServiceProvider { get; }
public BlobContainer(
string containerName,
BlobContainerConfiguration configuration,
IBlobProvider provider,
ICurrentTenant currentTenant,
ICancellationTokenProvider cancellationTokenProvider)
ICancellationTokenProvider cancellationTokenProvider,
IServiceProvider serviceProvider)
{
ContainerName = containerName;
Configuration = configuration;
Provider = provider;
CurrentTenant = currentTenant;
CancellationTokenProvider = cancellationTokenProvider;
ServiceProvider = serviceProvider;
}
private (string, string) NormalizeContainerNameAndBlobName(string containerName, string blobName)
{
if (!Configuration.NamingNormalizerProviders.Any())
{
return (containerName, blobName);
}
using (var scope = ServiceProvider.CreateScope())
{
foreach (var provider in Configuration.NamingNormalizerProviders)
{
var blobNamingNormalizerProvider = scope.ServiceProvider.GetRequiredService(provider)
.As<IBlobNamingNormalizerProvider>();
containerName = blobNamingNormalizerProvider.NormalizeContainerName(containerName);
blobName = blobNamingNormalizerProvider.NormalizeBlobName(blobName);
}
return (containerName, blobName);
}
}
public virtual async Task SaveAsync(
@ -106,11 +134,14 @@ namespace Volo.Abp.BlobStoring
{
using (CurrentTenant.Change(GetTenantIdOrNull()))
{
var (normalizedContainerName, normalizedBlobName) =
NormalizeContainerNameAndBlobName(ContainerName, name);
await Provider.SaveAsync(
new BlobProviderSaveArgs(
ContainerName,
normalizedContainerName,
Configuration,
name,
normalizedBlobName,
stream,
overrideExisting,
CancellationTokenProvider.FallbackToProvider(cancellationToken)
@ -125,11 +156,14 @@ namespace Volo.Abp.BlobStoring
{
using (CurrentTenant.Change(GetTenantIdOrNull()))
{
var (normalizedContainerName, normalizedBlobName) =
NormalizeContainerNameAndBlobName(ContainerName, name);
return await Provider.DeleteAsync(
new BlobProviderDeleteArgs(
ContainerName,
normalizedContainerName,
Configuration,
name,
normalizedBlobName,
CancellationTokenProvider.FallbackToProvider(cancellationToken)
)
);
@ -142,11 +176,14 @@ namespace Volo.Abp.BlobStoring
{
using (CurrentTenant.Change(GetTenantIdOrNull()))
{
var (normalizedContainerName, normalizedBlobName) =
NormalizeContainerNameAndBlobName(ContainerName, name);
return await Provider.ExistsAsync(
new BlobProviderExistsArgs(
ContainerName,
normalizedContainerName,
Configuration,
name,
normalizedBlobName,
CancellationTokenProvider.FallbackToProvider(cancellationToken)
)
);
@ -158,7 +195,7 @@ namespace Volo.Abp.BlobStoring
CancellationToken cancellationToken = default)
{
var stream = await GetOrNullAsync(name, cancellationToken);
if (stream == null)
{
//TODO: Consider to throw some type of "not found" exception and handle on the HTTP status side
@ -175,11 +212,14 @@ namespace Volo.Abp.BlobStoring
{
using (CurrentTenant.Change(GetTenantIdOrNull()))
{
var (normalizedContainerName, normalizedBlobName) =
NormalizeContainerNameAndBlobName(ContainerName, name);
return await Provider.GetOrNullAsync(
new BlobProviderGetArgs(
ContainerName,
normalizedContainerName,
Configuration,
name,
normalizedBlobName,
CancellationTokenProvider.FallbackToProvider(cancellationToken)
)
);
@ -196,4 +236,4 @@ namespace Volo.Abp.BlobStoring
return CurrentTenant.Id;
}
}
}
}

10
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs

@ -1,6 +1,7 @@
using System;
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Volo.Abp.Collections;
namespace Volo.Abp.BlobStoring
{
@ -18,17 +19,20 @@ namespace Volo.Abp.BlobStoring
/// then the container is shared by all tenants in the system.
///
/// This can be <code>true</code> even if your application is not multi-tenant.
///
///
/// Default: true.
/// </summary>
public bool IsMultiTenant { get; set; } = true;
public ITypeList<IBlobNamingNormalizerProvider> NamingNormalizerProviders { get; }
[NotNull] private readonly Dictionary<string, object> _properties;
[CanBeNull] private readonly BlobContainerConfiguration _fallbackConfiguration;
public BlobContainerConfiguration(BlobContainerConfiguration fallbackConfiguration = null)
{
NamingNormalizerProviders = new TypeList<IBlobNamingNormalizerProvider>();
_fallbackConfiguration = fallbackConfiguration;
_properties = new Dictionary<string, object>();
}
@ -68,4 +72,4 @@ namespace Volo.Abp.BlobStoring
return this;
}
}
}
}

24
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs

@ -1,4 +1,5 @@
using Volo.Abp.DependencyInjection;
using System;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
@ -7,36 +8,41 @@ namespace Volo.Abp.BlobStoring
public class BlobContainerFactory : IBlobContainerFactory, ITransientDependency
{
protected IBlobProviderSelector ProviderSelector { get; }
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; }
protected ICurrentTenant CurrentTenant { get; }
protected ICancellationTokenProvider CancellationTokenProvider { get; }
protected IServiceProvider ServiceProvider { get; }
public BlobContainerFactory(
IBlobContainerConfigurationProvider configurationProvider,
ICurrentTenant currentTenant,
ICancellationTokenProvider cancellationTokenProvider,
IBlobProviderSelector providerSelector)
ICancellationTokenProvider cancellationTokenProvider,
IBlobProviderSelector providerSelector,
IServiceProvider serviceProvider)
{
ConfigurationProvider = configurationProvider;
CurrentTenant = currentTenant;
CancellationTokenProvider = cancellationTokenProvider;
ProviderSelector = providerSelector;
ServiceProvider = serviceProvider;
}
public virtual IBlobContainer Create(string name)
{
var configuration = ConfigurationProvider.Get(name);
return new BlobContainer(
name,
configuration,
ProviderSelector.Get(name),
CurrentTenant,
CancellationTokenProvider
CancellationTokenProvider,
ServiceProvider
);
}
}
}
}

9
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobNamingNormalizerProvider.cs

@ -0,0 +1,9 @@
namespace Volo.Abp.BlobStoring
{
public interface IBlobNamingNormalizerProvider
{
string NormalizeContainerName(string containerName);
string NormalizeBlobName(string blobName);
}
}

9
framework/src/Volo.Abp.Core/System/Runtime/IOSPlatformProvider.cs

@ -0,0 +1,9 @@
using System.Runtime.InteropServices;
namespace System.Runtime
{
public interface IOSPlatformProvider
{
OSPlatform GetCurrentOSPlatform();
}
}

29
framework/src/Volo.Abp.Core/System/Runtime/OSPlatformProvider.cs

@ -0,0 +1,29 @@
using System.Runtime.InteropServices;
using Volo.Abp.DependencyInjection;
namespace System.Runtime
{
public class OSPlatformProvider : IOSPlatformProvider, ITransientDependency
{
public OSPlatform GetCurrentOSPlatform()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
//MAC
return OSPlatform.OSX;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return OSPlatform.Linux;
}
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return OSPlatform.Windows;
}
throw new Exception("Cannot determine operating system!");
}
}
}

57
framework/test/Volo.Abp.BlobStoring.Azure.Tests/Volo/Abp/BlobStoring/Azure/DefaultAzureBlobNamingNormalizerProvider_Tests.cs

@ -0,0 +1,57 @@
using Shouldly;
using Xunit;
namespace Volo.Abp.BlobStoring.Azure
{
public class DefaultAzureBlobNamingNormalizerProvider_Tests : AbpBlobStoringAzureTestCommonBase
{
private readonly IBlobNamingNormalizerProvider _blobNamingNormalizerProvider;
public DefaultAzureBlobNamingNormalizerProvider_Tests()
{
_blobNamingNormalizerProvider = GetRequiredService<IBlobNamingNormalizerProvider>();
}
[Fact]
public void NormalizeContainerName_Lowercase()
{
var filename = "ThisIsMyContainerName";
filename = _blobNamingNormalizerProvider.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 = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.ShouldBe("this-is-my-container-name");
}
[Fact]
public void NormalizeContainerName_Dash()
{
var filename = "-this--is----my-container----name-";
filename = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.ShouldBe("this-is-my-container-name");
}
[Fact]
public void NormalizeContainerName_Min_Length()
{
var filename = "a";
filename = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.Length.ShouldBeGreaterThanOrEqualTo(3);
}
[Fact]
public void NormalizeContainerName_Max_Length()
{
var filename = "abpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabpabp";
filename = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.Length.ShouldBeLessThanOrEqualTo(63);
}
}
}

62
framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/DefaultFileSystemBlobNamingNormalizerProvider_Tests.cs

@ -0,0 +1,62 @@
using System.Runtime;
using System.Runtime.InteropServices;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using Shouldly;
using Xunit;
namespace Volo.Abp.BlobStoring.FileSystem
{
public class DefaultFileSystemBlobNamingNormalizerProvider_Tests : AbpBlobStoringFileSystemTestBase
{
private readonly IBlobNamingNormalizerProvider _blobNamingNormalizerProvider;
public DefaultFileSystemBlobNamingNormalizerProvider_Tests()
{
_blobNamingNormalizerProvider = GetRequiredService<IBlobNamingNormalizerProvider>();
}
protected override void AfterAddApplication(IServiceCollection services)
{
var _iosPlatformProvider = Substitute.For<IOSPlatformProvider>();
_iosPlatformProvider.GetCurrentOSPlatform().Returns(OSPlatform.Windows);
services.AddSingleton(_iosPlatformProvider);
}
[Fact]
public void NormalizeContainerName()
{
var filename = "thisismy:*?\"<>|foldername";
filename = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.ShouldBe("thisismyfoldername");
}
[Fact]
public void NormalizeBlobName()
{
var filename = "thisismy:*?\"<>|filename";
filename = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.ShouldBe("thisismyfilename");
}
[Theory]
[InlineData("/")]
[InlineData("\\")]
public void NormalizeContainerName_With_Directory(string delimiter)
{
var filename = $"thisis{delimiter}my:*?\"<>|{delimiter}foldername";
filename = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.ShouldBe($"thisis{delimiter}my{delimiter}foldername");
}
[Theory]
[InlineData("/")]
[InlineData("\\")]
public void NormalizeBlobName_With_Directory(string delimiter)
{
var filename = $"thisis{delimiter}my:*?\"<>|{delimiter}filename";
filename = _blobNamingNormalizerProvider.NormalizeContainerName(filename);
filename.ShouldBe($"thisis{delimiter}my{delimiter}filename");
}
}
}
Loading…
Cancel
Save