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