mirror of https://github.com/abpframework/abp.git
24 changed files with 581 additions and 38 deletions
@ -1,9 +1,19 @@ |
|||||
using System.Collections.Generic; |
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
namespace Volo.Abp.BlobStoring |
namespace Volo.Abp.BlobStoring |
||||
{ |
{ |
||||
public class BlobContainerConfiguration : Dictionary<string, object> |
public class BlobContainerConfiguration : Dictionary<string, object> |
||||
{ |
{ |
||||
|
[NotNull] |
||||
|
public string Name { get; } |
||||
|
|
||||
|
public Type ProviderType { get; set; } |
||||
|
|
||||
|
public BlobContainerConfiguration([NotNull] string name) |
||||
|
{ |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
@ -1,22 +1,50 @@ |
|||||
using System; |
using System; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
namespace Volo.Abp.BlobStoring |
namespace Volo.Abp.BlobStoring |
||||
{ |
{ |
||||
public class BlobContainerConfigurationDictionary : Dictionary<string, BlobContainerConfiguration> |
public class BlobContainerConfigurationDictionary : Dictionary<string, BlobContainerConfiguration> |
||||
{ |
{ |
||||
public BlobContainerConfigurationDictionary Configure<TContainer>(Action<BlobContainerConfiguration> configureAction) |
public BlobContainerConfiguration Default { get; } |
||||
|
|
||||
|
public BlobContainerConfigurationDictionary() |
||||
|
{ |
||||
|
Default = new BlobContainerConfiguration("_default"); |
||||
|
} |
||||
|
|
||||
|
public BlobContainerConfigurationDictionary Configure<TContainer>( |
||||
|
Action<BlobContainerConfiguration> configureAction) |
||||
{ |
{ |
||||
return Configure( |
return Configure( |
||||
BlobContainerNameAttribute.GetContainerName<TContainer>(), |
BlobContainerNameAttribute.GetContainerName<TContainer>(), |
||||
configureAction |
configureAction |
||||
); |
); |
||||
} |
} |
||||
|
|
||||
public BlobContainerConfigurationDictionary Configure(string name, Action<BlobContainerConfiguration> configureAction) |
public BlobContainerConfigurationDictionary Configure( |
||||
|
[NotNull] string name, |
||||
|
[NotNull] Action<BlobContainerConfiguration> configureAction) |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(name, nameof(name)); |
||||
|
Check.NotNull(configureAction, nameof(configureAction)); |
||||
|
|
||||
|
configureAction(this.GetOrAdd(name, () => new BlobContainerConfiguration(name))); |
||||
|
|
||||
|
return this; |
||||
|
} |
||||
|
|
||||
|
public BlobContainerConfigurationDictionary ConfigureDefault(Action<BlobContainerConfiguration> configureAction) |
||||
{ |
{ |
||||
configureAction(this.GetOrAdd(name, () => new BlobContainerConfiguration())); |
configureAction(Default); |
||||
return this; |
return this; |
||||
} |
} |
||||
|
|
||||
|
public BlobContainerConfiguration GetOrDefaultConfiguration(string name) |
||||
|
{ |
||||
|
return AbpDictionaryExtensions.GetOrDefault(this, name) ?? |
||||
|
Default; |
||||
|
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.BlobStoring.Providers; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.DynamicProxy; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring |
||||
|
{ |
||||
|
public class BlobContainerFactory : IBlobContainerFactory, ITransientDependency |
||||
|
{ |
||||
|
public IEnumerable<IBlobProvider> BlobProviders { get; } |
||||
|
|
||||
|
protected AbpBlobStoringOptions Options { get; } |
||||
|
|
||||
|
public BlobContainerFactory( |
||||
|
IOptions<AbpBlobStoringOptions> options, |
||||
|
IEnumerable<IBlobProvider> blobProviders) |
||||
|
{ |
||||
|
BlobProviders = blobProviders; |
||||
|
Options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public virtual IBlobContainer Create(string name, CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var configuration = Options.Containers.GetOrDefaultConfiguration(name); |
||||
|
return new BlobContainerToProviderAdapter( |
||||
|
name, |
||||
|
configuration, |
||||
|
GetProvider(configuration) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
[NotNull] |
||||
|
protected virtual IBlobProvider GetProvider(BlobContainerConfiguration configuration) |
||||
|
{ |
||||
|
if (!BlobProviders.Any()) |
||||
|
{ |
||||
|
throw new AbpException("No BLOB Storage provider was registered! At least one provider must be registered to be able to use the Blog Storing System."); |
||||
|
} |
||||
|
|
||||
|
foreach (var provider in BlobProviders) |
||||
|
{ |
||||
|
if (ProxyHelper.GetUnProxiedType(provider).IsAssignableTo(configuration.ProviderType)) |
||||
|
{ |
||||
|
return provider; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
throw new AbpException( |
||||
|
$"Could not find the BLOB Storage provider with the type ({configuration.ProviderType.AssemblyQualifiedName}) configured for the container {configuration.Name}" |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
using System.IO; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Providers |
||||
|
{ |
||||
|
public class BlobContainerToProviderAdapter : IBlobContainer |
||||
|
{ |
||||
|
protected string ContainerName { get; } |
||||
|
|
||||
|
protected BlobContainerConfiguration ContainerConfiguration { get; } |
||||
|
|
||||
|
protected IBlobProvider Provider { get; } |
||||
|
|
||||
|
public BlobContainerToProviderAdapter( |
||||
|
string containerName, |
||||
|
BlobContainerConfiguration containerConfiguration, |
||||
|
IBlobProvider provider) |
||||
|
{ |
||||
|
ContainerName = containerName; |
||||
|
ContainerConfiguration = containerConfiguration; |
||||
|
Provider = provider; |
||||
|
} |
||||
|
|
||||
|
public virtual Task SaveAsync( |
||||
|
string name, |
||||
|
Stream stream, |
||||
|
bool overrideExisting = false, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Provider.SaveAsync( |
||||
|
new BlobProviderSaveArgs( |
||||
|
ContainerName, |
||||
|
ContainerConfiguration, |
||||
|
name, |
||||
|
stream, |
||||
|
overrideExisting, |
||||
|
cancellationToken |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<bool> DeleteAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Provider.DeleteAsync( |
||||
|
new BlobProviderDeleteArgs( |
||||
|
ContainerName, |
||||
|
ContainerConfiguration, |
||||
|
name, |
||||
|
cancellationToken |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<bool> ExistsAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Provider.ExistsAsync( |
||||
|
new BlobProviderExistsArgs( |
||||
|
ContainerName, |
||||
|
ContainerConfiguration, |
||||
|
name, |
||||
|
cancellationToken |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<Stream> GetAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Provider.GetAsync( |
||||
|
new BlobProviderGetArgs( |
||||
|
ContainerName, |
||||
|
ContainerConfiguration, |
||||
|
name, |
||||
|
cancellationToken |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<Stream> GetOrNullAsync( |
||||
|
string name, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
return Provider.GetOrNullAsync( |
||||
|
new BlobProviderGetArgs( |
||||
|
ContainerName, |
||||
|
ContainerConfiguration, |
||||
|
name, |
||||
|
cancellationToken |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using System.Threading; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Providers |
||||
|
{ |
||||
|
public abstract class BlobProviderArgs |
||||
|
{ |
||||
|
[NotNull] |
||||
|
public string ContainerName { get; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public BlobContainerConfiguration Configuration { get; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public string BlobName { get; } |
||||
|
|
||||
|
public CancellationToken CancellationToken { get; } |
||||
|
|
||||
|
protected BlobProviderArgs( |
||||
|
[NotNull] string containerName, |
||||
|
[NotNull] BlobContainerConfiguration configuration, |
||||
|
[NotNull] string blobName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
ContainerName = Check.NotNullOrWhiteSpace(containerName, nameof(containerName)); |
||||
|
Configuration = Check.NotNull(configuration, nameof(configuration)); |
||||
|
BlobName = Check.NotNullOrWhiteSpace(blobName, nameof(blobName)); |
||||
|
CancellationToken = cancellationToken; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System.Threading; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Providers |
||||
|
{ |
||||
|
public class BlobProviderDeleteArgs : BlobProviderArgs |
||||
|
{ |
||||
|
public BlobProviderDeleteArgs( |
||||
|
[NotNull] string containerName, |
||||
|
[NotNull] BlobContainerConfiguration configuration, |
||||
|
[NotNull] string blobName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
: base( |
||||
|
containerName, |
||||
|
configuration, |
||||
|
blobName, |
||||
|
cancellationToken) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System.Threading; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Providers |
||||
|
{ |
||||
|
public class BlobProviderExistsArgs : BlobProviderArgs |
||||
|
{ |
||||
|
public BlobProviderExistsArgs( |
||||
|
[NotNull] string containerName, |
||||
|
[NotNull] BlobContainerConfiguration configuration, |
||||
|
[NotNull] string blobName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
: base( |
||||
|
containerName, |
||||
|
configuration, |
||||
|
blobName, |
||||
|
cancellationToken) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using System.Threading; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Providers |
||||
|
{ |
||||
|
public class BlobProviderGetArgs : BlobProviderArgs |
||||
|
{ |
||||
|
public BlobProviderGetArgs( |
||||
|
[NotNull] string containerName, |
||||
|
[NotNull] BlobContainerConfiguration configuration, |
||||
|
[NotNull] string blobName, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
: base( |
||||
|
containerName, |
||||
|
configuration, |
||||
|
blobName, |
||||
|
cancellationToken) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
using System.IO; |
||||
|
using System.Threading; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Providers |
||||
|
{ |
||||
|
public class BlobProviderSaveArgs |
||||
|
{ |
||||
|
[NotNull] |
||||
|
public string ContainerName { get; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public BlobContainerConfiguration Configuration { get; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public string BlobName { get; } |
||||
|
|
||||
|
[NotNull] |
||||
|
public Stream BlobStream { get; } |
||||
|
|
||||
|
public bool OverrideExisting { get; } |
||||
|
|
||||
|
public CancellationToken CancellationToken { get; } |
||||
|
|
||||
|
public BlobProviderSaveArgs( |
||||
|
[NotNull] string containerName, |
||||
|
[NotNull] BlobContainerConfiguration configuration, |
||||
|
[NotNull] string blobName, |
||||
|
[NotNull] Stream blobStream, |
||||
|
bool overrideExisting = false, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
ContainerName = Check.NotNullOrWhiteSpace(containerName, nameof(containerName)); |
||||
|
Configuration = Check.NotNull(configuration, nameof(configuration)); |
||||
|
BlobName = Check.NotNullOrWhiteSpace(blobName, nameof(blobName)); |
||||
|
BlobStream = Check.NotNull(blobStream, nameof(blobStream)); |
||||
|
OverrideExisting = overrideExisting; |
||||
|
CancellationToken = cancellationToken; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Providers |
||||
|
{ |
||||
|
public interface IBlobProvider |
||||
|
{ |
||||
|
Task SaveAsync(BlobProviderSaveArgs args); |
||||
|
|
||||
|
Task<bool> DeleteAsync(BlobProviderDeleteArgs args); |
||||
|
|
||||
|
Task<bool> ExistsAsync(BlobProviderExistsArgs args); |
||||
|
|
||||
|
Task<Stream> GetAsync(BlobProviderGetArgs args); |
||||
|
|
||||
|
Task<Stream> GetOrNullAsync(BlobProviderGetArgs args); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using NSubstitute; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.BlobStoring.Fakes; |
||||
|
using Volo.Abp.BlobStoring.Providers; |
||||
|
using Volo.Abp.BlobStoring.TestObjects; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring |
||||
|
{ |
||||
|
public class BlobContainerFactory_Tests : AbpBlobStoringTestBase |
||||
|
{ |
||||
|
private readonly IBlobContainerFactory _factory; |
||||
|
private readonly FakeProviders _fakeProviders; |
||||
|
|
||||
|
public BlobContainerFactory_Tests() |
||||
|
{ |
||||
|
_factory = GetRequiredService<IBlobContainerFactory>(); |
||||
|
_fakeProviders = GetRequiredService<FakeProviders>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Create_Containers_With_Configured_Providers() |
||||
|
{ |
||||
|
// TestContainer1 with FakeBlobProvider1
|
||||
|
|
||||
|
await _fakeProviders.Provider1 |
||||
|
.DidNotReceiveWithAnyArgs() |
||||
|
.ExistsAsync(default); |
||||
|
|
||||
|
await _factory |
||||
|
.Create<TestContainer1>() |
||||
|
.ExistsAsync("TestBlob1"); |
||||
|
|
||||
|
await _fakeProviders.Provider1 |
||||
|
.Received(1) |
||||
|
.ExistsAsync(Arg.Is<BlobProviderExistsArgs>(args => |
||||
|
args.ContainerName == BlobContainerNameAttribute.GetContainerName<TestContainer1>() && |
||||
|
args.BlobName == "TestBlob1" |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
// TestContainer2 with FakeBlobProvider2
|
||||
|
|
||||
|
await _fakeProviders.Provider2 |
||||
|
.DidNotReceiveWithAnyArgs() |
||||
|
.ExistsAsync(default); |
||||
|
|
||||
|
await _factory |
||||
|
.Create<TestContainer2>() |
||||
|
.ExistsAsync("TestBlob2"); |
||||
|
|
||||
|
await _fakeProviders.Provider2 |
||||
|
.Received(1) |
||||
|
.ExistsAsync(Arg.Is<BlobProviderExistsArgs>(args => |
||||
|
args.ContainerName == BlobContainerNameAttribute.GetContainerName<TestContainer2>() && |
||||
|
args.BlobName == "TestBlob2" |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
// TestContainer3 with FakeBlobProvider1 (default provider)
|
||||
|
|
||||
|
_fakeProviders.Provider1.ClearReceivedCalls(); |
||||
|
|
||||
|
await _factory |
||||
|
.Create<TestContainer3>() |
||||
|
.ExistsAsync("TestBlob3"); |
||||
|
|
||||
|
await _fakeProviders.Provider1 |
||||
|
.Received(1) |
||||
|
.ExistsAsync(Arg.Is<BlobProviderExistsArgs>(t => |
||||
|
t.ContainerName == BlobContainerNameAttribute.GetContainerName<TestContainer3>() && |
||||
|
t.BlobName == "TestBlob3" |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BlobStoring.Providers; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Fakes |
||||
|
{ |
||||
|
public class FakeBlobProvider1 : IBlobProvider |
||||
|
{ |
||||
|
public virtual Task SaveAsync(BlobProviderSaveArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<Stream> GetAsync(BlobProviderGetArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.BlobStoring.Providers; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Fakes |
||||
|
{ |
||||
|
public class FakeBlobProvider2 : IBlobProvider |
||||
|
{ |
||||
|
public virtual Task SaveAsync(BlobProviderSaveArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<Stream> GetAsync(BlobProviderGetArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
|
||||
|
public virtual Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
||||
|
{ |
||||
|
throw new System.NotImplementedException(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Volo.Abp.BlobStoring.Providers; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.BlobStoring.Fakes |
||||
|
{ |
||||
|
public class FakeProviders : ISingletonDependency |
||||
|
{ |
||||
|
public FakeBlobProvider1 Provider1 { get; } |
||||
|
|
||||
|
public FakeBlobProvider2 Provider2 { get; } |
||||
|
|
||||
|
public FakeProviders(IEnumerable<IBlobProvider> providers) |
||||
|
{ |
||||
|
Provider1 = providers.OfType<FakeBlobProvider1>().Single(); |
||||
|
Provider2 = providers.OfType<FakeBlobProvider2>().Single(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,12 +0,0 @@ |
|||||
namespace Volo.Abp.BlobStoring |
|
||||
{ |
|
||||
public class TestContainer1 |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
|
|
||||
public class TestContainer2 |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.BlobStoring.TestObjects |
||||
|
{ |
||||
|
public class TestContainer1 |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace Volo.Abp.BlobStoring.TestObjects |
||||
|
{ |
||||
|
[BlobContainerName("Test2")] |
||||
|
public class TestContainer2 |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.BlobStoring.TestObjects |
||||
|
{ |
||||
|
public class TestContainer3 |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue