mirror of https://github.com/abpframework/abp.git
committed by
GitHub
62 changed files with 1772 additions and 4 deletions
@ -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.FileSystem</AssemblyName> |
|||
<PackageId>Volo.Abp.BlobStoring.FileSystem</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" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,13 @@ |
|||
using System; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpBlobStoringModule) |
|||
)] |
|||
public class AbpBlobStoringFileSystemModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System.IO; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
public class DefaultBlogFilePathCalculator : IBlogFilePathCalculator, ITransientDependency |
|||
{ |
|||
public virtual string Calculate(BlobProviderArgs args) |
|||
{ |
|||
var fileSystemConfiguration = args.Configuration.GetFileSystemConfiguration(); |
|||
var blobPath = fileSystemConfiguration.BasePath; |
|||
|
|||
if (args.TenantId == null) |
|||
{ |
|||
blobPath = Path.Combine(blobPath, "host"); |
|||
} |
|||
else |
|||
{ |
|||
blobPath = Path.Combine(blobPath, "tenants", args.TenantId.Value.ToString("D")); |
|||
} |
|||
|
|||
if (fileSystemConfiguration.AppendContainerNameToBasePath) |
|||
{ |
|||
blobPath = Path.Combine(blobPath, args.ContainerName); |
|||
} |
|||
|
|||
blobPath = Path.Combine(blobPath, args.BlobName); |
|||
|
|||
return blobPath; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
public static class FileSystemBlobContainerConfigurationExtensions |
|||
{ |
|||
public static FileSystemBlobProviderConfiguration GetFileSystemConfiguration( |
|||
this BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
return new FileSystemBlobProviderConfiguration(containerConfiguration); |
|||
} |
|||
|
|||
public static BlobContainerConfiguration UseFileSystem( |
|||
this BlobContainerConfiguration containerConfiguration, |
|||
Action<FileSystemBlobProviderConfiguration> fileSystemConfigureAction) |
|||
{ |
|||
containerConfiguration.ProviderType = typeof(FileSystemBlobProvider); |
|||
|
|||
fileSystemConfigureAction(new FileSystemBlobProviderConfiguration(containerConfiguration)); |
|||
|
|||
return containerConfiguration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.IO; |
|||
|
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
//TODO: What if the file is being used on create, delete or read?
|
|||
|
|||
public class FileSystemBlobProvider : BlobProviderBase, ITransientDependency |
|||
{ |
|||
protected IBlogFilePathCalculator FilePathCalculator { get; } |
|||
|
|||
public FileSystemBlobProvider(IBlogFilePathCalculator filePathCalculator) |
|||
{ |
|||
FilePathCalculator = filePathCalculator; |
|||
} |
|||
|
|||
public override async Task SaveAsync(BlobProviderSaveArgs args) |
|||
{ |
|||
var filePath = FilePathCalculator.Calculate(args); |
|||
|
|||
DirectoryHelper.CreateIfNotExists(Path.GetDirectoryName(filePath)); |
|||
|
|||
var fileMode = args.OverrideExisting |
|||
? FileMode.Create |
|||
: FileMode.CreateNew; |
|||
|
|||
using (var fileStream = File.Open(filePath, fileMode, FileAccess.Write)) |
|||
{ |
|||
//TODO: Truely implement this (like this? http://writeasync.net/?p=2621 or https://www.infoworld.com/article/2995387/how-to-perform-asynchronous-file-operations-in-c.html)
|
|||
|
|||
await args.BlobStream.CopyToAsync( |
|||
fileStream, |
|||
81920, //this is already the default value, but needed to set to be able to pass the cancellationToken
|
|||
args.CancellationToken |
|||
); |
|||
|
|||
await fileStream.FlushAsync(); |
|||
} |
|||
} |
|||
|
|||
public override Task<bool> DeleteAsync(BlobProviderDeleteArgs args) |
|||
{ |
|||
var filePath = FilePathCalculator.Calculate(args); |
|||
|
|||
return Task.FromResult(FileHelper.DeleteIfExists(filePath)); |
|||
} |
|||
|
|||
public override Task<bool> ExistsAsync(BlobProviderExistsArgs args) |
|||
{ |
|||
var filePath = FilePathCalculator.Calculate(args); |
|||
|
|||
return Task.FromResult(File.Exists(filePath)); |
|||
} |
|||
|
|||
public override Task<Stream> GetOrNullAsync(BlobProviderGetArgs args) |
|||
{ |
|||
var filePath = FilePathCalculator.Calculate(args); |
|||
|
|||
if (!File.Exists(filePath)) |
|||
{ |
|||
return Task.FromResult<Stream>(null); |
|||
} |
|||
|
|||
return Task.FromResult<Stream>(File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
public class FileSystemBlobProviderConfiguration |
|||
{ |
|||
|
|||
public string BasePath |
|||
{ |
|||
get => _containerConfiguration.GetConfiguration<string>(FileSystemBlobProviderConfigurationNames.BasePath); |
|||
set => _containerConfiguration.SetConfiguration(FileSystemBlobProviderConfigurationNames.BasePath, Check.NotNullOrWhiteSpace(value, nameof(value))); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Default value: true.
|
|||
/// </summary>
|
|||
public bool AppendContainerNameToBasePath |
|||
{ |
|||
get => _containerConfiguration.GetConfigurationOrDefault(FileSystemBlobProviderConfigurationNames.AppendContainerNameToBasePath, true); |
|||
set => _containerConfiguration.SetConfiguration(FileSystemBlobProviderConfigurationNames.AppendContainerNameToBasePath, value); |
|||
} |
|||
|
|||
private readonly BlobContainerConfiguration _containerConfiguration; |
|||
|
|||
public FileSystemBlobProviderConfiguration(BlobContainerConfiguration containerConfiguration) |
|||
{ |
|||
_containerConfiguration = containerConfiguration; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
public static class FileSystemBlobProviderConfigurationNames |
|||
{ |
|||
public const string BasePath = "FileSystem.BasePath"; |
|||
public const string AppendContainerNameToBasePath = "FileSystem.AppendContainerNameToBasePath"; |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
public interface IBlogFilePathCalculator |
|||
{ |
|||
string Calculate(BlobProviderArgs 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,23 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<AssemblyName>Volo.Abp.BlobStoring</AssemblyName> |
|||
<PackageId>Volo.Abp.BlobStoring</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.Core\Volo.Abp.Core.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.MultiTenancy\Volo.Abp.MultiTenancy.csproj" /> |
|||
<ProjectReference Include="..\Volo.Abp.Threading\Volo.Abp.Threading.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,28 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpMultiTenancyModule), |
|||
typeof(AbpThreadingModule) |
|||
)] |
|||
public class AbpBlobStoringModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddTransient( |
|||
typeof(IBlobContainer<>), |
|||
typeof(BlobContainer<>) |
|||
); |
|||
|
|||
context.Services.AddTransient( |
|||
typeof(IBlobContainer), |
|||
serviceProvider => serviceProvider |
|||
.GetRequiredService<IBlobContainer<DefaultContainer>>() |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class AbpBlobStoringOptions |
|||
{ |
|||
public BlobContainerConfigurations Containers { get; } |
|||
|
|||
public AbpBlobStoringOptions() |
|||
{ |
|||
Containers = new BlobContainerConfigurations(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,198 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Threading; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobContainer<TContainer> : IBlobContainer<TContainer> |
|||
where TContainer : class |
|||
{ |
|||
private readonly IBlobContainer _container; |
|||
|
|||
public BlobContainer(IBlobContainerFactory blobContainerFactory) |
|||
{ |
|||
_container = blobContainerFactory.Create<TContainer>(); |
|||
} |
|||
|
|||
public Task SaveAsync( |
|||
string name, |
|||
Stream stream, |
|||
bool overrideExisting = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _container.SaveAsync( |
|||
name, |
|||
stream, |
|||
overrideExisting, |
|||
cancellationToken |
|||
); |
|||
} |
|||
|
|||
public Task<bool> DeleteAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _container.DeleteAsync( |
|||
name, |
|||
cancellationToken |
|||
); |
|||
} |
|||
|
|||
public Task<bool> ExistsAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _container.ExistsAsync( |
|||
name, |
|||
cancellationToken |
|||
); |
|||
} |
|||
|
|||
public Task<Stream> GetAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _container.GetAsync( |
|||
name, |
|||
cancellationToken |
|||
); |
|||
} |
|||
|
|||
public Task<Stream> GetOrNullAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return _container.GetOrNullAsync( |
|||
name, |
|||
cancellationToken |
|||
); |
|||
} |
|||
} |
|||
|
|||
public class BlobContainer : IBlobContainer |
|||
{ |
|||
protected string ContainerName { get; } |
|||
|
|||
protected BlobContainerConfiguration Configuration { get; } |
|||
|
|||
protected IBlobProvider Provider { get; } |
|||
|
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
|
|||
protected ICancellationTokenProvider CancellationTokenProvider { get; } |
|||
|
|||
public BlobContainer( |
|||
string containerName, |
|||
BlobContainerConfiguration configuration, |
|||
IBlobProvider provider, |
|||
ICurrentTenant currentTenant, |
|||
ICancellationTokenProvider cancellationTokenProvider) |
|||
{ |
|||
ContainerName = containerName; |
|||
Configuration = configuration; |
|||
Provider = provider; |
|||
CurrentTenant = currentTenant; |
|||
CancellationTokenProvider = cancellationTokenProvider; |
|||
} |
|||
|
|||
public virtual Task SaveAsync( |
|||
string name, |
|||
Stream stream, |
|||
bool overrideExisting = false, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return Provider.SaveAsync( |
|||
new BlobProviderSaveArgs( |
|||
ContainerName, |
|||
Configuration, |
|||
name, |
|||
stream, |
|||
overrideExisting, |
|||
GetTenantIdOrNull(), |
|||
CancellationTokenProvider.FallbackToProvider(cancellationToken) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public virtual Task<bool> DeleteAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return Provider.DeleteAsync( |
|||
new BlobProviderDeleteArgs( |
|||
ContainerName, |
|||
Configuration, |
|||
name, |
|||
GetTenantIdOrNull(), |
|||
CancellationTokenProvider.FallbackToProvider(cancellationToken) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public virtual Task<bool> ExistsAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return Provider.ExistsAsync( |
|||
new BlobProviderExistsArgs( |
|||
ContainerName, |
|||
Configuration, |
|||
name, |
|||
GetTenantIdOrNull(), |
|||
CancellationTokenProvider.FallbackToProvider(cancellationToken) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public virtual Task<Stream> GetAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
var stream = Provider.GetOrNullAsync( |
|||
new BlobProviderGetArgs( |
|||
ContainerName, |
|||
Configuration, |
|||
name, |
|||
GetTenantIdOrNull(), |
|||
CancellationTokenProvider.FallbackToProvider(cancellationToken) |
|||
) |
|||
); |
|||
|
|||
if (stream == null) |
|||
{ |
|||
//TODO: Consider to throw some type of "not found" exception and handle on the HTTP status side
|
|||
throw new AbpException($"Could not found the requested BLOB '{name}' in the container '{ContainerName}'!"); |
|||
} |
|||
|
|||
return stream; |
|||
} |
|||
|
|||
public virtual Task<Stream> GetOrNullAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return Provider.GetOrNullAsync( |
|||
new BlobProviderGetArgs( |
|||
ContainerName, |
|||
Configuration, |
|||
name, |
|||
GetTenantIdOrNull(), |
|||
CancellationTokenProvider.FallbackToProvider(cancellationToken) |
|||
) |
|||
); |
|||
} |
|||
|
|||
protected virtual Guid? GetTenantIdOrNull() |
|||
{ |
|||
if (!Configuration.IsMultiTenant) |
|||
{ |
|||
return null; |
|||
} |
|||
|
|||
return CurrentTenant.Id; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobContainerConfiguration |
|||
{ |
|||
/// <summary>
|
|||
/// The provider to be used to store BLOBs of this container.
|
|||
/// </summary>
|
|||
public Type ProviderType { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Indicates whether this container is multi-tenant or not.
|
|||
///
|
|||
/// If this is <code>false</code> and your application is multi-tenant,
|
|||
/// 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; |
|||
|
|||
[NotNull] private readonly Dictionary<string, object> _properties; |
|||
|
|||
[CanBeNull] private readonly BlobContainerConfiguration _fallbackConfiguration; |
|||
|
|||
public BlobContainerConfiguration(BlobContainerConfiguration fallbackConfiguration = null) |
|||
{ |
|||
_fallbackConfiguration = fallbackConfiguration; |
|||
_properties = new Dictionary<string, object>(); |
|||
} |
|||
|
|||
[CanBeNull] |
|||
public T GetConfigurationOrDefault<T>(string name, T defaultValue = default) |
|||
{ |
|||
return (T) GetConfigurationOrNull(name, defaultValue); |
|||
} |
|||
|
|||
[CanBeNull] |
|||
public object GetConfigurationOrNull(string name, object defaultValue = null) |
|||
{ |
|||
return _properties.GetOrDefault(name) ?? |
|||
_fallbackConfiguration?.GetConfigurationOrNull(name, defaultValue) ?? |
|||
defaultValue; |
|||
} |
|||
|
|||
[NotNull] |
|||
public BlobContainerConfiguration SetConfiguration([NotNull] string name, [CanBeNull] object value) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
Check.NotNull(value, nameof(value)); |
|||
|
|||
_properties[name] = value; |
|||
|
|||
return this; |
|||
} |
|||
|
|||
[NotNull] |
|||
public BlobContainerConfiguration ClearConfiguration([NotNull] string name) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
|
|||
_properties.Remove(name); |
|||
|
|||
return this; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public static class BlobContainerConfigurationExtensions |
|||
{ |
|||
public static T GetConfiguration<T>( |
|||
[NotNull] this BlobContainerConfiguration containerConfiguration, |
|||
[NotNull] string name) |
|||
{ |
|||
return (T) containerConfiguration.GetConfiguration(name); |
|||
} |
|||
|
|||
public static object GetConfiguration( |
|||
[NotNull] this BlobContainerConfiguration containerConfiguration, |
|||
[NotNull] string name) |
|||
{ |
|||
var value = containerConfiguration.GetConfigurationOrNull(name); |
|||
if (value == null) |
|||
{ |
|||
throw new AbpException($"Could not find the configuration value for '{name}'!"); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public static class BlobContainerConfigurationProviderExtensions |
|||
{ |
|||
public static BlobContainerConfiguration Get<TContainer>( |
|||
this IBlobContainerConfigurationProvider configurationProvider) |
|||
{ |
|||
return configurationProvider.Get(BlobContainerNameAttribute.GetContainerName<TContainer>()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobContainerConfigurations |
|||
{ |
|||
public BlobContainerConfiguration Default { get; } |
|||
|
|||
private readonly Dictionary<string, BlobContainerConfiguration> _containers; |
|||
|
|||
public BlobContainerConfigurations() |
|||
{ |
|||
Default = new BlobContainerConfiguration(); |
|||
_containers = new Dictionary<string, BlobContainerConfiguration>(); |
|||
} |
|||
|
|||
public BlobContainerConfigurations Configure<TContainer>( |
|||
Action<BlobContainerConfiguration> configureAction) |
|||
{ |
|||
return Configure( |
|||
BlobContainerNameAttribute.GetContainerName<TContainer>(), |
|||
configureAction |
|||
); |
|||
} |
|||
|
|||
public BlobContainerConfigurations Configure( |
|||
[NotNull] string name, |
|||
[NotNull] Action<BlobContainerConfiguration> configureAction) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
Check.NotNull(configureAction, nameof(configureAction)); |
|||
|
|||
configureAction(_containers.GetOrAdd(name, () => new BlobContainerConfiguration(Default))); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public BlobContainerConfigurations ConfigureDefault(Action<BlobContainerConfiguration> configureAction) |
|||
{ |
|||
configureAction(Default); |
|||
return this; |
|||
} |
|||
|
|||
[NotNull] |
|||
public BlobContainerConfiguration GetConfiguration<TContainer>() |
|||
{ |
|||
return GetConfiguration(BlobContainerNameAttribute.GetContainerName<TContainer>()); |
|||
} |
|||
|
|||
[NotNull] |
|||
public BlobContainerConfiguration GetConfiguration([NotNull] string name) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
|
|||
return _containers.GetOrDefault(name) ?? |
|||
Default; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using System.Threading; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Threading; |
|||
|
|||
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; } |
|||
|
|||
public BlobContainerFactory( |
|||
IBlobContainerConfigurationProvider configurationProvider, |
|||
ICurrentTenant currentTenant, |
|||
ICancellationTokenProvider cancellationTokenProvider, |
|||
IBlobProviderSelector providerSelector) |
|||
{ |
|||
ConfigurationProvider = configurationProvider; |
|||
CurrentTenant = currentTenant; |
|||
CancellationTokenProvider = cancellationTokenProvider; |
|||
ProviderSelector = providerSelector; |
|||
} |
|||
|
|||
public virtual IBlobContainer Create(string name, CancellationToken cancellationToken = default) |
|||
{ |
|||
var configuration = ConfigurationProvider.Get(name); |
|||
|
|||
return new BlobContainer( |
|||
name, |
|||
configuration, |
|||
ProviderSelector.Get(name), |
|||
CurrentTenant, |
|||
CancellationTokenProvider |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
using System.Threading; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public static class BlobContainerFactoryExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets a named container.
|
|||
/// </summary>
|
|||
/// <param name="blobContainerFactory">The blob container manager</param>
|
|||
/// <param name="cancellationToken">Cancellation token</param>
|
|||
/// <returns>
|
|||
/// The container object.
|
|||
/// </returns>
|
|||
public static IBlobContainer Create<TContainer>( |
|||
this IBlobContainerFactory blobContainerFactory, |
|||
CancellationToken cancellationToken = default |
|||
) |
|||
{ |
|||
return blobContainerFactory.Create( |
|||
BlobContainerNameAttribute.GetContainerName<TContainer>(), |
|||
cancellationToken |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Reflection; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobContainerNameAttribute : Attribute |
|||
{ |
|||
[NotNull] |
|||
public string Name { get; } |
|||
|
|||
public BlobContainerNameAttribute([NotNull] string name) |
|||
{ |
|||
Check.NotNullOrWhiteSpace(name, nameof(name)); |
|||
|
|||
Name = name; |
|||
} |
|||
|
|||
public virtual string GetName(Type type) |
|||
{ |
|||
return Name; |
|||
} |
|||
|
|||
public static string GetContainerName<T>() |
|||
{ |
|||
return GetContainerName(typeof(T)); |
|||
} |
|||
|
|||
public static string GetContainerName(Type type) |
|||
{ |
|||
var nameAttribute = type.GetCustomAttribute<BlobContainerNameAttribute>(); |
|||
|
|||
if (nameAttribute == null) |
|||
{ |
|||
return type.FullName; |
|||
} |
|||
|
|||
return nameAttribute.GetName(type); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public abstract class BlobProviderArgs |
|||
{ |
|||
[NotNull] |
|||
public string ContainerName { get; } |
|||
|
|||
[NotNull] |
|||
public BlobContainerConfiguration Configuration { get; } |
|||
|
|||
[NotNull] |
|||
public string BlobName { get; } |
|||
|
|||
public CancellationToken CancellationToken { get; } |
|||
|
|||
public Guid? TenantId { get; } |
|||
|
|||
protected BlobProviderArgs( |
|||
[NotNull] string containerName, |
|||
[NotNull] BlobContainerConfiguration configuration, |
|||
[NotNull] string blobName, |
|||
[CanBeNull] Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
ContainerName = Check.NotNullOrWhiteSpace(containerName, nameof(containerName)); |
|||
Configuration = Check.NotNull(configuration, nameof(configuration)); |
|||
BlobName = Check.NotNullOrWhiteSpace(blobName, nameof(blobName)); |
|||
TenantId = tenantId; |
|||
CancellationToken = cancellationToken; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public abstract class BlobProviderBase : IBlobProvider |
|||
{ |
|||
public abstract Task SaveAsync(BlobProviderSaveArgs args); |
|||
|
|||
public abstract Task<bool> DeleteAsync(BlobProviderDeleteArgs args); |
|||
|
|||
public abstract Task<bool> ExistsAsync(BlobProviderExistsArgs args); |
|||
|
|||
public abstract Task<Stream> GetOrNullAsync(BlobProviderGetArgs args); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobProviderDeleteArgs : BlobProviderArgs |
|||
{ |
|||
public BlobProviderDeleteArgs( |
|||
[NotNull] string containerName, |
|||
[NotNull] BlobContainerConfiguration configuration, |
|||
[NotNull] string blobName, |
|||
[CanBeNull] Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
: base( |
|||
containerName, |
|||
configuration, |
|||
blobName, |
|||
tenantId, |
|||
cancellationToken) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobProviderExistsArgs : BlobProviderArgs |
|||
{ |
|||
public BlobProviderExistsArgs( |
|||
[NotNull] string containerName, |
|||
[NotNull] BlobContainerConfiguration configuration, |
|||
[NotNull] string blobName, |
|||
[CanBeNull] Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
: base( |
|||
containerName, |
|||
configuration, |
|||
blobName, |
|||
tenantId, |
|||
cancellationToken) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobProviderGetArgs : BlobProviderArgs |
|||
{ |
|||
public BlobProviderGetArgs( |
|||
[NotNull] string containerName, |
|||
[NotNull] BlobContainerConfiguration configuration, |
|||
[NotNull] string blobName, |
|||
[CanBeNull] Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
: base( |
|||
containerName, |
|||
configuration, |
|||
blobName, |
|||
tenantId, |
|||
cancellationToken) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobProviderSaveArgs : BlobProviderArgs |
|||
{ |
|||
[NotNull] |
|||
public Stream BlobStream { get; } |
|||
|
|||
public bool OverrideExisting { get; } |
|||
|
|||
public BlobProviderSaveArgs( |
|||
[NotNull] string containerName, |
|||
[NotNull] BlobContainerConfiguration configuration, |
|||
[NotNull] string blobName, |
|||
[NotNull] Stream blobStream, |
|||
bool overrideExisting = false, |
|||
[CanBeNull] Guid? tenantId = null, |
|||
CancellationToken cancellationToken = default) |
|||
: base( |
|||
containerName, |
|||
configuration, |
|||
blobName, |
|||
tenantId, |
|||
cancellationToken) |
|||
{ |
|||
BlobStream = Check.NotNull(blobStream, nameof(blobStream)); |
|||
OverrideExisting = overrideExisting; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public static class BlobProviderSelectorExtensions |
|||
{ |
|||
public static IBlobProvider Get<TContainer>( |
|||
[NotNull] this IBlobProviderSelector selector) |
|||
{ |
|||
Check.NotNull(selector, nameof(selector)); |
|||
|
|||
return selector.Get(BlobContainerNameAttribute.GetContainerName<TContainer>()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class DefaultBlobContainerConfigurationProvider : IBlobContainerConfigurationProvider, ITransientDependency |
|||
{ |
|||
protected AbpBlobStoringOptions Options { get; } |
|||
|
|||
public DefaultBlobContainerConfigurationProvider(IOptions<AbpBlobStoringOptions> options) |
|||
{ |
|||
Options = options.Value; |
|||
} |
|||
|
|||
public virtual BlobContainerConfiguration Get(string name) |
|||
{ |
|||
return Options.Containers.GetConfiguration(name); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using JetBrains.Annotations; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.DynamicProxy; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class DefaultBlobProviderSelector : IBlobProviderSelector, ITransientDependency |
|||
{ |
|||
protected IEnumerable<IBlobProvider> BlobProviders { get; } |
|||
|
|||
protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } |
|||
|
|||
public DefaultBlobProviderSelector( |
|||
IBlobContainerConfigurationProvider configurationProvider, |
|||
IEnumerable<IBlobProvider> blobProviders) |
|||
{ |
|||
ConfigurationProvider = configurationProvider; |
|||
BlobProviders = blobProviders; |
|||
} |
|||
|
|||
[NotNull] |
|||
public virtual IBlobProvider Get([NotNull] string containerName) |
|||
{ |
|||
Check.NotNull(containerName, nameof(containerName)); |
|||
|
|||
var configuration = ConfigurationProvider.Get(containerName); |
|||
|
|||
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 {containerName} and no default provider was set." |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
[BlobContainerName("Default")] |
|||
public class DefaultContainer |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
using System.IO; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public interface IBlobContainer<TContainer> : IBlobContainer |
|||
where TContainer: class |
|||
{ |
|||
|
|||
} |
|||
|
|||
public interface IBlobContainer |
|||
{ |
|||
/// <summary>
|
|||
/// Saves a blob <see cref="Stream"/> to the container.
|
|||
/// </summary>
|
|||
/// <param name="name">The name of the blob</param>
|
|||
/// <param name="stream">A stream for the blob</param>
|
|||
/// <param name="overrideExisting">
|
|||
/// Set <code>true</code> to override if there is already a blob in the container with the given name.
|
|||
/// If set to <code>false</code> (default), throws exception if there is already a blob in the container with the given name.
|
|||
/// </param>
|
|||
/// <param name="cancellationToken">Cancellation token</param>
|
|||
Task SaveAsync( |
|||
string name, |
|||
Stream stream, |
|||
bool overrideExisting = false, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
/// <summary>
|
|||
/// Deletes a blob from the container.
|
|||
/// </summary>
|
|||
/// <param name="name">The name of the blob</param>
|
|||
/// <param name="cancellationToken">Cancellation token</param>
|
|||
/// <returns>
|
|||
/// Returns true if actually deleted the blob.
|
|||
/// Returns false if the blob with the given <paramref name="name"/> was not exists.
|
|||
/// </returns>
|
|||
Task<bool> DeleteAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
/// <summary>
|
|||
/// Checks if a blob does exists in the container.
|
|||
/// </summary>
|
|||
/// <param name="name">The name of the blob</param>
|
|||
/// <param name="cancellationToken">Cancellation token</param>
|
|||
Task<bool> ExistsAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
/// <summary>
|
|||
/// Gets a blob from the container.
|
|||
/// It actually gets a <see cref="Stream"/> to read the blob data.
|
|||
/// It throws exception if there is no blob with the given <paramref name="name"/>.
|
|||
/// Use <see cref="GetOrNullAsync"/> if you want to get <code>null</code> if there is no blob with the given <paramref name="name"/>.
|
|||
/// </summary>
|
|||
/// <param name="name">The name of the blob</param>
|
|||
/// <param name="cancellationToken">Cancellation token</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Stream"/> to read the blob data.
|
|||
/// </returns>
|
|||
Task<Stream> GetAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
/// <summary>
|
|||
/// Gets a blob from the container, or returns null if there is no blob with the given <paramref name="name"/>.
|
|||
/// It actually gets a <see cref="Stream"/> to read the blob data.
|
|||
/// </summary>
|
|||
/// <param name="name">The name of the blob</param>
|
|||
/// <param name="cancellationToken">Cancellation token</param>
|
|||
/// <returns>
|
|||
/// A <see cref="Stream"/> to read the blob data.
|
|||
/// </returns>
|
|||
Task<Stream> GetOrNullAsync( |
|||
string name, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
|
|||
//TODO: Create shortcut extension methods: GetAsArraryAsync, GetAsStringAsync(encoding) (and null versions)
|
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public interface IBlobContainerConfigurationProvider |
|||
{ |
|||
BlobContainerConfiguration Get(string name); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System.Threading; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public interface IBlobContainerFactory |
|||
{ |
|||
/// <summary>
|
|||
/// Gets a named container.
|
|||
/// </summary>
|
|||
/// <param name="name">The name of the container</param>
|
|||
/// <param name="cancellationToken">Cancellation token</param>
|
|||
/// <returns>
|
|||
/// The container object.
|
|||
/// </returns>
|
|||
IBlobContainer Create( |
|||
string name, |
|||
CancellationToken cancellationToken = default |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public interface IBlobProvider |
|||
{ |
|||
Task SaveAsync(BlobProviderSaveArgs args); |
|||
|
|||
Task<bool> DeleteAsync(BlobProviderDeleteArgs args); |
|||
|
|||
Task<bool> ExistsAsync(BlobProviderExistsArgs args); |
|||
|
|||
Task<Stream> GetOrNullAsync(BlobProviderGetArgs args); |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public interface IBlobProviderSelector |
|||
{ |
|||
[NotNull] |
|||
IBlobProvider Get([NotNull] string containerName); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.BlobStoring.FileSystem\Volo.Abp.BlobStoring.FileSystem.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.IO; |
|||
using Volo.Abp.BlobStoring.FileSystem.TestObjects; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpBlobStoringFileSystemModule) |
|||
)] |
|||
public class AbpBlobStoringFileSystemTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpBlobStoringOptions>(options => |
|||
{ |
|||
options.Containers.ConfigureDefault(container => |
|||
{ |
|||
container.UseFileSystem(fileSystem => |
|||
{ |
|||
fileSystem.BasePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); |
|||
}); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
public abstract class AbpBlobStoringFileSystemTestBase : AbpIntegratedTest<AbpBlobStoringFileSystemTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,71 @@ |
|||
using System; |
|||
using System.IO; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring.FileSystem |
|||
{ |
|||
public class BlogFilePathCalculator_Tests : AbpBlobStoringFileSystemTestBase |
|||
{ |
|||
private readonly IBlogFilePathCalculator _calculator; |
|||
|
|||
public BlogFilePathCalculator_Tests() |
|||
{ |
|||
_calculator = GetRequiredService<IBlogFilePathCalculator>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Default_Settings() |
|||
{ |
|||
var separator = Path.DirectorySeparatorChar; |
|||
|
|||
_calculator.Calculate( |
|||
GetArgs($"C:{separator}my-files", "my-container", "my-blob") |
|||
).ShouldBe($"C:{separator}my-files{separator}host{separator}my-container{separator}my-blob"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Default_Settings_With_TenantId() |
|||
{ |
|||
var separator = Path.DirectorySeparatorChar; |
|||
var tenantId = Guid.NewGuid(); |
|||
|
|||
_calculator.Calculate( |
|||
GetArgs($"C:{separator}my-files", "my-container", "my-blob", tenantId: tenantId) |
|||
).ShouldBe($"C:{separator}my-files{separator}tenants{separator}{tenantId:D}{separator}my-container{separator}my-blob"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AppendContainerNameToBasePath_Set_To_False() |
|||
{ |
|||
var separator = Path.DirectorySeparatorChar; |
|||
|
|||
_calculator.Calculate( |
|||
GetArgs($"C:{separator}my-files", "my-container", "my-blob", appendContainerNameToBasePath: false) |
|||
).ShouldBe($"C:{separator}my-files{separator}host{separator}my-blob"); |
|||
} |
|||
|
|||
private BlobProviderArgs GetArgs( |
|||
string basePath, |
|||
string containerName, |
|||
string blobName, |
|||
Guid? tenantId = null, |
|||
bool? appendContainerNameToBasePath = null) |
|||
{ |
|||
return new BlobProviderGetArgs( |
|||
containerName, |
|||
new BlobContainerConfiguration() |
|||
.UseFileSystem(fs => |
|||
{ |
|||
fs.BasePath = basePath; |
|||
if (appendContainerNameToBasePath.HasValue) |
|||
{ |
|||
fs.AppendContainerNameToBasePath = appendContainerNameToBasePath.Value; |
|||
} |
|||
}), |
|||
blobName, |
|||
tenantId |
|||
); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Volo.Abp.BlobStoring.FileSystem.TestObjects |
|||
{ |
|||
[BlobContainerName("TestContainer")] |
|||
public class TestContainer |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.test.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netcoreapp3.1</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" /> |
|||
<ProjectReference Include="..\..\src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj" /> |
|||
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" /> |
|||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,41 @@ |
|||
using System.Collections.Generic; |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using Volo.Abp.BlobStoring.Fakes; |
|||
using Volo.Abp.BlobStoring.TestObjects; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class AbpBlobStoringOptions_Tests : AbpBlobStoringTestBase |
|||
{ |
|||
private readonly IBlobContainerConfigurationProvider _configurationProvider; |
|||
|
|||
public AbpBlobStoringOptions_Tests() |
|||
{ |
|||
_configurationProvider = GetRequiredService<IBlobContainerConfigurationProvider>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Property_Set_And_Get_Options_For_Different_Containers() |
|||
{ |
|||
var testContainer1Config = _configurationProvider.Get<TestContainer1>(); |
|||
testContainer1Config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); |
|||
testContainer1Config.GetConfigurationOrDefault<string>("TestConfig1").ShouldBe("TestValue1"); |
|||
testContainer1Config.GetConfigurationOrDefault<string>("TestConfigDefault").ShouldBe("TestValueDefault"); |
|||
|
|||
var testContainer2Config = _configurationProvider.Get<TestContainer2>(); |
|||
testContainer2Config.ProviderType.ShouldBe(typeof(FakeBlobProvider2)); |
|||
testContainer2Config.GetConfigurationOrNull("TestConfig2").ShouldBe("TestValue2"); |
|||
testContainer2Config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Fallback_To_Default_Configuration_If_Not_Specialized() |
|||
{ |
|||
var config = _configurationProvider.Get<TestContainer3>(); |
|||
config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); |
|||
config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.Testing; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public abstract class AbpBlobStoringTestBase : AbpIntegratedTest<AbpBlobStoringTestModule> |
|||
{ |
|||
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) |
|||
{ |
|||
options.UseAutofac(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,43 @@ |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using NSubstitute; |
|||
using Volo.Abp.Autofac; |
|||
using Volo.Abp.BlobStoring.Fakes; |
|||
using Volo.Abp.BlobStoring.TestObjects; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
[DependsOn( |
|||
typeof(AbpBlobStoringModule), |
|||
typeof(AbpTestBaseModule), |
|||
typeof(AbpAutofacModule) |
|||
)] |
|||
public class AbpBlobStoringTestModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddSingleton<IBlobProvider>(Substitute.For<FakeBlobProvider1>()); |
|||
context.Services.AddSingleton<IBlobProvider>(Substitute.For<FakeBlobProvider2>()); |
|||
|
|||
Configure<AbpBlobStoringOptions>(options => |
|||
{ |
|||
options.Containers |
|||
.ConfigureDefault(container => |
|||
{ |
|||
container.SetConfiguration("TestConfigDefault", "TestValueDefault"); |
|||
container.ProviderType = typeof(FakeBlobProvider1); |
|||
}) |
|||
.Configure<TestContainer1>(container => |
|||
{ |
|||
container.SetConfiguration("TestConfig1", "TestValue1"); |
|||
container.ProviderType = typeof(FakeBlobProvider1); |
|||
}) |
|||
.Configure<TestContainer2>(container => |
|||
{ |
|||
container.SetConfiguration("TestConfig2", "TestValue2"); |
|||
container.ProviderType = typeof(FakeBlobProvider2); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
using System.Threading.Tasks; |
|||
using NSubstitute; |
|||
using Volo.Abp.BlobStoring.Fakes; |
|||
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,25 @@ |
|||
using Shouldly; |
|||
using Volo.Abp.BlobStoring.TestObjects; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobContainerNameAttribute_Tests |
|||
{ |
|||
[Fact] |
|||
public void Should_Get_Specified_Name() |
|||
{ |
|||
BlobContainerNameAttribute |
|||
.GetContainerName<TestContainer2>() |
|||
.ShouldBe("Test2"); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Get_Full_Class_Name_If_Not_Specified() |
|||
{ |
|||
BlobContainerNameAttribute |
|||
.GetContainerName<TestContainer1>() |
|||
.ShouldBe(typeof(TestContainer1).FullName); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using Shouldly; |
|||
using Volo.Abp.BlobStoring.TestObjects; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobContainer_Injection_Tests : AbpBlobStoringTestBase |
|||
{ |
|||
[Fact] |
|||
public void Should_Inject_DefaultContainer_For_Non_Generic_Interface() |
|||
{ |
|||
GetRequiredService<IBlobContainer>() |
|||
.ShouldBeOfType<BlobContainer<DefaultContainer>>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Inject_Specified_Container_For_Generic_Interface() |
|||
{ |
|||
GetRequiredService<IBlobContainer<DefaultContainer>>() |
|||
.ShouldBeOfType<BlobContainer<DefaultContainer>>(); |
|||
|
|||
GetRequiredService<IBlobContainer<TestContainer1>>() |
|||
.ShouldBeOfType<BlobContainer<TestContainer1>>(); |
|||
|
|||
GetRequiredService<IBlobContainer<TestContainer2>>() |
|||
.ShouldBeOfType<BlobContainer<TestContainer2>>(); |
|||
|
|||
GetRequiredService<IBlobContainer<TestContainer3>>() |
|||
.ShouldBeOfType<BlobContainer<TestContainer3>>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using Shouldly; |
|||
using Volo.Abp.BlobStoring.Fakes; |
|||
using Volo.Abp.BlobStoring.TestObjects; |
|||
using Volo.Abp.DynamicProxy; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.BlobStoring |
|||
{ |
|||
public class BlobProviderSelector_Tests : AbpBlobStoringTestBase |
|||
{ |
|||
private readonly IBlobProviderSelector _selector; |
|||
|
|||
public BlobProviderSelector_Tests() |
|||
{ |
|||
_selector = GetRequiredService<IBlobProviderSelector>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Select_Default_Provider_If_Not_Configured() |
|||
{ |
|||
_selector.Get<TestContainer3>().ShouldBeAssignableTo<FakeBlobProvider1>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_Select_Configured_Provider() |
|||
{ |
|||
_selector.Get<TestContainer1>().ShouldBeAssignableTo<FakeBlobProvider1>(); |
|||
_selector.Get<TestContainer2>().ShouldBeAssignableTo<FakeBlobProvider2>(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
|
|||
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,33 @@ |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
|
|||
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,19 @@ |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
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(); |
|||
} |
|||
} |
|||
} |
|||
@ -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