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 |
|||
{ |
|||
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.Collections.Generic; |
|||
using System.IO; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
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( |
|||
BlobContainerNameAttribute.GetContainerName<TContainer>(), |
|||
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; |
|||
} |
|||
|
|||
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