diff --git a/framework/Volo.Abp.sln b/framework/Volo.Abp.sln index 8e8dac2ce5..165e62334c 100644 --- a/framework/Volo.Abp.sln +++ b/framework/Volo.Abp.sln @@ -291,6 +291,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.AspNetCore.SignalR EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests", "test\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests\Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.csproj", "{79323211-E658-493E-9863-035AA4C3F913}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring", "src\Volo.Abp.BlobStoring\Volo.Abp.BlobStoring.csproj", "{A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.Tests", "test\Volo.Abp.BlobStoring.Tests\Volo.Abp.BlobStoring.Tests.csproj", "{D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem", "src\Volo.Abp.BlobStoring.FileSystem\Volo.Abp.BlobStoring.FileSystem.csproj", "{02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.BlobStoring.FileSystem.Tests", "test\Volo.Abp.BlobStoring.FileSystem.Tests\Volo.Abp.BlobStoring.FileSystem.Tests.csproj", "{68443D4A-1608-4039-B995-7AF4CF82E9F8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -865,6 +873,22 @@ Global {79323211-E658-493E-9863-035AA4C3F913}.Debug|Any CPU.Build.0 = Debug|Any CPU {79323211-E658-493E-9863-035AA4C3F913}.Release|Any CPU.ActiveCfg = Release|Any CPU {79323211-E658-493E-9863-035AA4C3F913}.Release|Any CPU.Build.0 = Release|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42}.Release|Any CPU.Build.0 = Release|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958}.Release|Any CPU.Build.0 = Release|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B}.Release|Any CPU.Build.0 = Release|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68443D4A-1608-4039-B995-7AF4CF82E9F8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -1012,6 +1036,10 @@ Global {B64FCE08-E9D2-4984-BF12-FE199F257416} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} {8B758716-DCC9-4223-8421-5588D1597487} = {447C8A77-E5F0-4538-8687-7383196D04EA} {79323211-E658-493E-9863-035AA4C3F913} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {A0CFBDD6-A3CB-438C-83F1-5025F12E2D42} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {D53A17BB-4E23-451D-AD9B-E1F6AC3F7958} = {447C8A77-E5F0-4538-8687-7383196D04EA} + {02B1FBE2-850E-4612-ABC6-DD62BCF2DD6B} = {5DF0E140-0513-4D0D-BE2E-3D4D85CD70E6} + {68443D4A-1608-4039-B995-7AF4CF82E9F8} = {447C8A77-E5F0-4538-8687-7383196D04EA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5} diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xsd b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj new file mode 100644 index 0000000000..03fcbbd374 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo.Abp.BlobStoring.FileSystem.csproj @@ -0,0 +1,21 @@ + + + + + + + netstandard2.0 + Volo.Abp.BlobStoring.FileSystem + Volo.Abp.BlobStoring.FileSystem + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemModule.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemModule.cs new file mode 100644 index 0000000000..249bbfaa78 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemModule.cs @@ -0,0 +1,13 @@ +using System; +using Volo.Abp.Modularity; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + [DependsOn( + typeof(AbpBlobStoringModule) + )] + public class AbpBlobStoringFileSystemModule : AbpModule + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultBlogFilePathCalculator.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultBlogFilePathCalculator.cs new file mode 100644 index 0000000000..62c2585375 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/DefaultBlogFilePathCalculator.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..24357554eb --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobContainerConfigurationExtensions.cs @@ -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 fileSystemConfigureAction) + { + containerConfiguration.ProviderType = typeof(FileSystemBlobProvider); + + fileSystemConfigureAction(new FileSystemBlobProviderConfiguration(containerConfiguration)); + + return containerConfiguration; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs new file mode 100644 index 0000000000..b927bdbf47 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProvider.cs @@ -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 DeleteAsync(BlobProviderDeleteArgs args) + { + var filePath = FilePathCalculator.Calculate(args); + + return Task.FromResult(FileHelper.DeleteIfExists(filePath)); + } + + public override Task ExistsAsync(BlobProviderExistsArgs args) + { + var filePath = FilePathCalculator.Calculate(args); + + return Task.FromResult(File.Exists(filePath)); + } + + public override Task GetOrNullAsync(BlobProviderGetArgs args) + { + var filePath = FilePathCalculator.Calculate(args); + + if (!File.Exists(filePath)) + { + return Task.FromResult(null); + } + + return Task.FromResult(File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfiguration.cs new file mode 100644 index 0000000000..4d4a786d16 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfiguration.cs @@ -0,0 +1,28 @@ +namespace Volo.Abp.BlobStoring.FileSystem +{ + public class FileSystemBlobProviderConfiguration + { + + public string BasePath + { + get => _containerConfiguration.GetConfiguration(FileSystemBlobProviderConfigurationNames.BasePath); + set => _containerConfiguration.SetConfiguration(FileSystemBlobProviderConfigurationNames.BasePath, Check.NotNullOrWhiteSpace(value, nameof(value))); + } + + /// + /// Default value: true. + /// + 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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfigurationNames.cs new file mode 100644 index 0000000000..56991fa0ea --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/FileSystemBlobProviderConfigurationNames.cs @@ -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"; + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/IBlogFilePathCalculator.cs b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/IBlogFilePathCalculator.cs new file mode 100644 index 0000000000..9e4ec4713a --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring.FileSystem/Volo/Abp/BlobStoring/FileSystem/IBlogFilePathCalculator.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.FileSystem +{ + public interface IBlogFilePathCalculator + { + string Calculate(BlobProviderArgs args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xml b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xml new file mode 100644 index 0000000000..be0de3a908 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xsd b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xsd new file mode 100644 index 0000000000..3f3946e282 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/FodyWeavers.xsd @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. + + + + + A comma-separated list of error codes that can be safely ignored in assembly verification. + + + + + 'false' to turn off automatic generation of the XML Schema file. + + + + + \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo.Abp.BlobStoring.csproj b/framework/src/Volo.Abp.BlobStoring/Volo.Abp.BlobStoring.csproj new file mode 100644 index 0000000000..850f98b5f1 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo.Abp.BlobStoring.csproj @@ -0,0 +1,23 @@ + + + + + + + netstandard2.0 + Volo.Abp.BlobStoring + Volo.Abp.BlobStoring + $(AssetTargetFallback);portable-net45+win8+wp8+wpa81; + false + false + false + + + + + + + + + + diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringModule.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringModule.cs new file mode 100644 index 0000000000..35970ea169 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringModule.cs @@ -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>() + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringOptions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringOptions.cs new file mode 100644 index 0000000000..4ed0ce69a0 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/AbpBlobStoringOptions.cs @@ -0,0 +1,12 @@ +namespace Volo.Abp.BlobStoring +{ + public class AbpBlobStoringOptions + { + public BlobContainerConfigurations Containers { get; } + + public AbpBlobStoringOptions() + { + Containers = new BlobContainerConfigurations(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs new file mode 100644 index 0000000000..700549e957 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainer.cs @@ -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 : IBlobContainer + where TContainer : class + { + private readonly IBlobContainer _container; + + public BlobContainer(IBlobContainerFactory blobContainerFactory) + { + _container = blobContainerFactory.Create(); + } + + public Task SaveAsync( + string name, + Stream stream, + bool overrideExisting = false, + CancellationToken cancellationToken = default) + { + return _container.SaveAsync( + name, + stream, + overrideExisting, + cancellationToken + ); + } + + public Task DeleteAsync( + string name, + CancellationToken cancellationToken = default) + { + return _container.DeleteAsync( + name, + cancellationToken + ); + } + + public Task ExistsAsync( + string name, + CancellationToken cancellationToken = default) + { + return _container.ExistsAsync( + name, + cancellationToken + ); + } + + public Task GetAsync( + string name, + CancellationToken cancellationToken = default) + { + return _container.GetAsync( + name, + cancellationToken + ); + } + + public Task 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 DeleteAsync( + string name, + CancellationToken cancellationToken = default) + { + return Provider.DeleteAsync( + new BlobProviderDeleteArgs( + ContainerName, + Configuration, + name, + GetTenantIdOrNull(), + CancellationTokenProvider.FallbackToProvider(cancellationToken) + ) + ); + } + + public virtual Task ExistsAsync( + string name, + CancellationToken cancellationToken = default) + { + return Provider.ExistsAsync( + new BlobProviderExistsArgs( + ContainerName, + Configuration, + name, + GetTenantIdOrNull(), + CancellationTokenProvider.FallbackToProvider(cancellationToken) + ) + ); + } + + public virtual Task 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 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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs new file mode 100644 index 0000000000..46f13a1c3d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfiguration.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public class BlobContainerConfiguration + { + /// + /// The provider to be used to store BLOBs of this container. + /// + public Type ProviderType { get; set; } + + /// + /// Indicates whether this container is multi-tenant or not. + /// + /// If this is false and your application is multi-tenant, + /// then the container is shared by all tenants in the system. + /// + /// This can be true even if your application is not multi-tenant. + /// + /// Default: true. + /// + public bool IsMultiTenant { get; set; } = true; + + [NotNull] private readonly Dictionary _properties; + + [CanBeNull] private readonly BlobContainerConfiguration _fallbackConfiguration; + + public BlobContainerConfiguration(BlobContainerConfiguration fallbackConfiguration = null) + { + _fallbackConfiguration = fallbackConfiguration; + _properties = new Dictionary(); + } + + [CanBeNull] + public T GetConfigurationOrDefault(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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationExtensions.cs new file mode 100644 index 0000000000..ca437b0ad0 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationExtensions.cs @@ -0,0 +1,27 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public static class BlobContainerConfigurationExtensions + { + public static T GetConfiguration( + [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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationProviderExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationProviderExtensions.cs new file mode 100644 index 0000000000..a199d8a72f --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurationProviderExtensions.cs @@ -0,0 +1,11 @@ +namespace Volo.Abp.BlobStoring +{ + public static class BlobContainerConfigurationProviderExtensions + { + public static BlobContainerConfiguration Get( + this IBlobContainerConfigurationProvider configurationProvider) + { + return configurationProvider.Get(BlobContainerNameAttribute.GetContainerName()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurations.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurations.cs new file mode 100644 index 0000000000..5e17793f21 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerConfigurations.cs @@ -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 _containers; + + public BlobContainerConfigurations() + { + Default = new BlobContainerConfiguration(); + _containers = new Dictionary(); + } + + public BlobContainerConfigurations Configure( + Action configureAction) + { + return Configure( + BlobContainerNameAttribute.GetContainerName(), + configureAction + ); + } + + public BlobContainerConfigurations Configure( + [NotNull] string name, + [NotNull] Action configureAction) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + Check.NotNull(configureAction, nameof(configureAction)); + + configureAction(_containers.GetOrAdd(name, () => new BlobContainerConfiguration(Default))); + + return this; + } + + public BlobContainerConfigurations ConfigureDefault(Action configureAction) + { + configureAction(Default); + return this; + } + + [NotNull] + public BlobContainerConfiguration GetConfiguration() + { + return GetConfiguration(BlobContainerNameAttribute.GetContainerName()); + } + + [NotNull] + public BlobContainerConfiguration GetConfiguration([NotNull] string name) + { + Check.NotNullOrWhiteSpace(name, nameof(name)); + + return _containers.GetOrDefault(name) ?? + Default; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs new file mode 100644 index 0000000000..62f6aa7a73 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs @@ -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 + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs new file mode 100644 index 0000000000..34483c1849 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs @@ -0,0 +1,26 @@ +using System.Threading; + +namespace Volo.Abp.BlobStoring +{ + public static class BlobContainerFactoryExtensions + { + /// + /// Gets a named container. + /// + /// The blob container manager + /// Cancellation token + /// + /// The container object. + /// + public static IBlobContainer Create( + this IBlobContainerFactory blobContainerFactory, + CancellationToken cancellationToken = default + ) + { + return blobContainerFactory.Create( + BlobContainerNameAttribute.GetContainerName(), + cancellationToken + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerNameAttribute.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerNameAttribute.cs new file mode 100644 index 0000000000..12bd91d669 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerNameAttribute.cs @@ -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() + { + return GetContainerName(typeof(T)); + } + + public static string GetContainerName(Type type) + { + var nameAttribute = type.GetCustomAttribute(); + + if (nameAttribute == null) + { + return type.FullName; + } + + return nameAttribute.GetName(type); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs new file mode 100644 index 0000000000..e1ad228839 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderArgs.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderBase.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderBase.cs new file mode 100644 index 0000000000..b1aafb6903 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderBase.cs @@ -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 DeleteAsync(BlobProviderDeleteArgs args); + + public abstract Task ExistsAsync(BlobProviderExistsArgs args); + + public abstract Task GetOrNullAsync(BlobProviderGetArgs args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderDeleteArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderDeleteArgs.cs new file mode 100644 index 0000000000..aa4aae1eb2 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderDeleteArgs.cs @@ -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) + { + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderExistsArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderExistsArgs.cs new file mode 100644 index 0000000000..a55b22a233 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderExistsArgs.cs @@ -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) + { + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderGetArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderGetArgs.cs new file mode 100644 index 0000000000..94374698c7 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderGetArgs.cs @@ -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) + { + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSaveArgs.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSaveArgs.cs new file mode 100644 index 0000000000..a81938fb7d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSaveArgs.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSelectorExtensions.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSelectorExtensions.cs new file mode 100644 index 0000000000..1d484ebd2e --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobProviderSelectorExtensions.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public static class BlobProviderSelectorExtensions + { + public static IBlobProvider Get( + [NotNull] this IBlobProviderSelector selector) + { + Check.NotNull(selector, nameof(selector)); + + return selector.Get(BlobContainerNameAttribute.GetContainerName()); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobContainerConfigurationProvider.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobContainerConfigurationProvider.cs new file mode 100644 index 0000000000..a30d584c9d --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobContainerConfigurationProvider.cs @@ -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 options) + { + Options = options.Value; + } + + public virtual BlobContainerConfiguration Get(string name) + { + return Options.Containers.GetConfiguration(name); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs new file mode 100644 index 0000000000..5f604155ca --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultBlobProviderSelector.cs @@ -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 BlobProviders { get; } + + protected IBlobContainerConfigurationProvider ConfigurationProvider { get; } + + public DefaultBlobProviderSelector( + IBlobContainerConfigurationProvider configurationProvider, + IEnumerable 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." + ); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs new file mode 100644 index 0000000000..64f907e564 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.BlobStoring +{ + [BlobContainerName("Default")] + public class DefaultContainer + { + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainer.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainer.cs new file mode 100644 index 0000000000..a596565b5f --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainer.cs @@ -0,0 +1,88 @@ +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Volo.Abp.BlobStoring +{ + public interface IBlobContainer : IBlobContainer + where TContainer: class + { + + } + + public interface IBlobContainer + { + /// + /// Saves a blob to the container. + /// + /// The name of the blob + /// A stream for the blob + /// + /// Set true to override if there is already a blob in the container with the given name. + /// If set to false (default), throws exception if there is already a blob in the container with the given name. + /// + /// Cancellation token + Task SaveAsync( + string name, + Stream stream, + bool overrideExisting = false, + CancellationToken cancellationToken = default + ); + + /// + /// Deletes a blob from the container. + /// + /// The name of the blob + /// Cancellation token + /// + /// Returns true if actually deleted the blob. + /// Returns false if the blob with the given was not exists. + /// + Task DeleteAsync( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Checks if a blob does exists in the container. + /// + /// The name of the blob + /// Cancellation token + Task ExistsAsync( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Gets a blob from the container. + /// It actually gets a to read the blob data. + /// It throws exception if there is no blob with the given . + /// Use if you want to get null if there is no blob with the given . + /// + /// The name of the blob + /// Cancellation token + /// + /// A to read the blob data. + /// + Task GetAsync( + string name, + CancellationToken cancellationToken = default + ); + + /// + /// Gets a blob from the container, or returns null if there is no blob with the given . + /// It actually gets a to read the blob data. + /// + /// The name of the blob + /// Cancellation token + /// + /// A to read the blob data. + /// + Task GetOrNullAsync( + string name, + CancellationToken cancellationToken = default + ); + + //TODO: Create shortcut extension methods: GetAsArraryAsync, GetAsStringAsync(encoding) (and null versions) + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs new file mode 100644 index 0000000000..f984a903a2 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring +{ + public interface IBlobContainerConfigurationProvider + { + BlobContainerConfiguration Get(string name); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs new file mode 100644 index 0000000000..8434e3ae23 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs @@ -0,0 +1,20 @@ +using System.Threading; + +namespace Volo.Abp.BlobStoring +{ + public interface IBlobContainerFactory + { + /// + /// Gets a named container. + /// + /// The name of the container + /// Cancellation token + /// + /// The container object. + /// + IBlobContainer Create( + string name, + CancellationToken cancellationToken = default + ); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProvider.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProvider.cs new file mode 100644 index 0000000000..d7332589c5 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProvider.cs @@ -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 DeleteAsync(BlobProviderDeleteArgs args); + + Task ExistsAsync(BlobProviderExistsArgs args); + + Task GetOrNullAsync(BlobProviderGetArgs args); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProviderSelector.cs b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProviderSelector.cs new file mode 100644 index 0000000000..defb8f0804 --- /dev/null +++ b/framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobProviderSelector.cs @@ -0,0 +1,10 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.BlobStoring +{ + public interface IBlobProviderSelector + { + [NotNull] + IBlobProvider Get([NotNull] string containerName); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs index 96a1915e26..db7ff4b869 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/ProxyHelper.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using System.Reflection; namespace Volo.Abp.DynamicProxy @@ -6,6 +7,7 @@ namespace Volo.Abp.DynamicProxy public static class ProxyHelper { private const string ProxyNamespace = "Castle.Proxies"; + /// /// Returns dynamic proxy target object if this is a proxied object, otherwise returns the given object. /// It supports Castle Dynamic Proxies. @@ -28,5 +30,10 @@ namespace Volo.Abp.DynamicProxy return targetField.GetValue(obj); } + + public static Type GetUnProxiedType(object obj) + { + return UnProxy(obj).GetType(); + } } } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs index b864695430..23f8de610a 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/DirectoryHelper.cs @@ -16,6 +16,14 @@ namespace Volo.Abp.IO Directory.CreateDirectory(directory); } } + + public static void CreateIfNotExists(DirectoryInfo directory) + { + if (!directory.Exists) + { + directory.Create(); + } + } public static bool IsSubDirectoryOf([NotNull] string parentDirectoryPath, [NotNull] string childDirectoryPath) { diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs index a945784352..8f4a15bdf5 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/IO/FileHelper.cs @@ -17,12 +17,15 @@ namespace Volo.Abp.IO /// Checks and deletes given file if it does exists. /// /// Path of the file - public static void DeleteIfExists(string filePath) + public static bool DeleteIfExists(string filePath) { - if (File.Exists(filePath)) + if (!File.Exists(filePath)) { - File.Delete(filePath); + return false; } + + File.Delete(filePath); + return true; } /// diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj new file mode 100644 index 0000000000..1ac7ba2414 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo.Abp.BlobStoring.FileSystem.Tests.csproj @@ -0,0 +1,17 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemTestModule.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemTestModule.cs new file mode 100644 index 0000000000..35fc30b84d --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringFileSystemTestModule.cs @@ -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(options => + { + options.Containers.ConfigureDefault(container => + { + container.UseFileSystem(fileSystem => + { + fileSystem.BasePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); + }); + }); + }); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringTestBase.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringTestBase.cs new file mode 100644 index 0000000000..8fdd832a5b --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/AbpBlobStoringTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring.FileSystem +{ + public abstract class AbpBlobStoringFileSystemTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/BlogFilePathCalculator_Tests.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/BlogFilePathCalculator_Tests.cs new file mode 100644 index 0000000000..2493dab733 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/BlogFilePathCalculator_Tests.cs @@ -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(); + } + + [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 + ); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/TestObjects/TestContainer.cs b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/TestObjects/TestContainer.cs new file mode 100644 index 0000000000..91ac79f6c3 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.FileSystem.Tests/Volo/Abp/BlobStoring/FileSystem/TestObjects/TestContainer.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.BlobStoring.FileSystem.TestObjects +{ + [BlobContainerName("TestContainer")] + public class TestContainer + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj b/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj new file mode 100644 index 0000000000..d9535ff176 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo.Abp.BlobStoring.Tests.csproj @@ -0,0 +1,17 @@ + + + + + + netcoreapp3.1 + + + + + + + + + + + diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs new file mode 100644 index 0000000000..53c7c2079d --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringOptions_Tests.cs @@ -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(); + } + + [Fact] + public void Should_Property_Set_And_Get_Options_For_Different_Containers() + { + var testContainer1Config = _configurationProvider.Get(); + testContainer1Config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); + testContainer1Config.GetConfigurationOrDefault("TestConfig1").ShouldBe("TestValue1"); + testContainer1Config.GetConfigurationOrDefault("TestConfigDefault").ShouldBe("TestValueDefault"); + + var testContainer2Config = _configurationProvider.Get(); + 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(); + config.ProviderType.ShouldBe(typeof(FakeBlobProvider1)); + config.GetConfigurationOrNull("TestConfigDefault").ShouldBe("TestValueDefault"); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestBase.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestBase.cs new file mode 100644 index 0000000000..07e34c599f --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestBase.cs @@ -0,0 +1,12 @@ +using Volo.Abp.Testing; + +namespace Volo.Abp.BlobStoring +{ + public abstract class AbpBlobStoringTestBase : AbpIntegratedTest + { + protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) + { + options.UseAutofac(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs new file mode 100644 index 0000000000..0d150a20ea --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/AbpBlobStoringTestModule.cs @@ -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(Substitute.For()); + context.Services.AddSingleton(Substitute.For()); + + Configure(options => + { + options.Containers + .ConfigureDefault(container => + { + container.SetConfiguration("TestConfigDefault", "TestValueDefault"); + container.ProviderType = typeof(FakeBlobProvider1); + }) + .Configure(container => + { + container.SetConfiguration("TestConfig1", "TestValue1"); + container.ProviderType = typeof(FakeBlobProvider1); + }) + .Configure(container => + { + container.SetConfiguration("TestConfig2", "TestValue2"); + container.ProviderType = typeof(FakeBlobProvider2); + }); + }); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerFactory_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerFactory_Tests.cs new file mode 100644 index 0000000000..359470106e --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerFactory_Tests.cs @@ -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(); + _fakeProviders = GetRequiredService(); + } + + [Fact] + public async Task Should_Create_Containers_With_Configured_Providers() + { + // TestContainer1 with FakeBlobProvider1 + + await _fakeProviders.Provider1 + .DidNotReceiveWithAnyArgs() + .ExistsAsync(default); + + await _factory + .Create() + .ExistsAsync("TestBlob1"); + + await _fakeProviders.Provider1 + .Received(1) + .ExistsAsync(Arg.Is(args => + args.ContainerName == BlobContainerNameAttribute.GetContainerName() && + args.BlobName == "TestBlob1" + ) + ); + + // TestContainer2 with FakeBlobProvider2 + + await _fakeProviders.Provider2 + .DidNotReceiveWithAnyArgs() + .ExistsAsync(default); + + await _factory + .Create() + .ExistsAsync("TestBlob2"); + + await _fakeProviders.Provider2 + .Received(1) + .ExistsAsync(Arg.Is(args => + args.ContainerName == BlobContainerNameAttribute.GetContainerName() && + args.BlobName == "TestBlob2" + ) + ); + + // TestContainer3 with FakeBlobProvider1 (default provider) + + _fakeProviders.Provider1.ClearReceivedCalls(); + + await _factory + .Create() + .ExistsAsync("TestBlob3"); + + await _fakeProviders.Provider1 + .Received(1) + .ExistsAsync(Arg.Is(t => + t.ContainerName == BlobContainerNameAttribute.GetContainerName() && + t.BlobName == "TestBlob3" + ) + ); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs new file mode 100644 index 0000000000..242b195a0c --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainerNameAttribute_Tests.cs @@ -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() + .ShouldBe("Test2"); + } + + [Fact] + public void Should_Get_Full_Class_Name_If_Not_Specified() + { + BlobContainerNameAttribute + .GetContainerName() + .ShouldBe(typeof(TestContainer1).FullName); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Injection_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Injection_Tests.cs new file mode 100644 index 0000000000..bd4ec2c47a --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobContainer_Injection_Tests.cs @@ -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() + .ShouldBeOfType>(); + } + + [Fact] + public void Should_Inject_Specified_Container_For_Generic_Interface() + { + GetRequiredService>() + .ShouldBeOfType>(); + + GetRequiredService>() + .ShouldBeOfType>(); + + GetRequiredService>() + .ShouldBeOfType>(); + + GetRequiredService>() + .ShouldBeOfType>(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobProviderSelector_Tests.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobProviderSelector_Tests.cs new file mode 100644 index 0000000000..f36129c19d --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/BlobProviderSelector_Tests.cs @@ -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(); + } + + [Fact] + public void Should_Select_Default_Provider_If_Not_Configured() + { + _selector.Get().ShouldBeAssignableTo(); + } + + [Fact] + public void Should_Select_Configured_Provider() + { + _selector.Get().ShouldBeAssignableTo(); + _selector.Get().ShouldBeAssignableTo(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs new file mode 100644 index 0000000000..38fef18d66 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider1.cs @@ -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 DeleteAsync(BlobProviderDeleteArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task ExistsAsync(BlobProviderExistsArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetOrNullAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs new file mode 100644 index 0000000000..dc0e40d3d8 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeBlobProvider2.cs @@ -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 DeleteAsync(BlobProviderDeleteArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task ExistsAsync(BlobProviderExistsArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + + public virtual Task GetOrNullAsync(BlobProviderGetArgs args) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeProviders.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeProviders.cs new file mode 100644 index 0000000000..e66e8b6adc --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/Fakes/FakeProviders.cs @@ -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 providers) + { + Provider1 = providers.OfType().Single(); + Provider2 = providers.OfType().Single(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs new file mode 100644 index 0000000000..61c84a1148 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer1.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.TestObjects +{ + public class TestContainer1 + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs new file mode 100644 index 0000000000..3193da3749 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer2.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.BlobStoring.TestObjects +{ + [BlobContainerName("Test2")] + public class TestContainer2 + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs new file mode 100644 index 0000000000..b96a71c1b7 --- /dev/null +++ b/framework/test/Volo.Abp.BlobStoring.Tests/Volo/Abp/BlobStoring/TestObjects/TestContainer3.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.BlobStoring.TestObjects +{ + public class TestContainer3 + { + + } +} \ No newline at end of file diff --git a/nupkg/common.ps1 b/nupkg/common.ps1 index 8f1fb99536..d056ae032d 100644 --- a/nupkg/common.ps1 +++ b/nupkg/common.ps1 @@ -55,6 +55,8 @@ $projects = ( "framework/src/Volo.Abp.BackgroundJobs.Quartz", "framework/src/Volo.Abp.BackgroundWorkers", "framework/src/Volo.Abp.BackgroundWorkers.Quartz", + "framework/src/Volo.Abp.BlobStoring", + "framework/src/Volo.Abp.BlobStoring.FileSystem", "framework/src/Volo.Abp.Caching", "framework/src/Volo.Abp.Castle.Core", "framework/src/Volo.Abp.Cli.Core",