Browse Source

Merge branch 'master' into dev

pull/4290/head
Halil İbrahim Kalkan 6 years ago
parent
commit
85c770c3f1
  1. 3
      docs/en/Blob-Storing-Custom-Provider.md
  2. 227
      docs/en/Blob-Storing.md
  3. 30
      docs/en/docs-nav.json
  4. 5
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs
  5. 10
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs
  6. 4
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs
  7. 5
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs
  8. 8
      framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs

3
docs/en/Blob-Storing-Custom-Provider.md

@ -0,0 +1,3 @@
# BLOB Storing: Creating a Custom Provider
TODO

227
docs/en/Blob-Storing.md

@ -1,6 +1,6 @@
# BLOB Storing # BLOB Storing
It is typical to **store file contents** in an application and read these file contents on need. Not only files, but you may also need to save various types of **large binary objects**, a.k.a. **[BLOB](https://en.wikipedia.org/wiki/Binary_large_object)** into a **storage**. For example, you may want to save user profile pictures. It is typical to **store file contents** in an application and read these file contents on need. Not only files, but you may also need to save various types of **large binary objects**, a.k.a. **[BLOB](https://en.wikipedia.org/wiki/Binary_large_object)**s, into a **storage**. For example, you may want to save user profile pictures.
A BLOB is typically a **byte array**. Storing a BLOB in the local file system, in a shared database or on the [Azure BLOB storage](https://azure.microsoft.com/en-us/services/storage/blobs/) can be an option. A BLOB is typically a **byte array**. Storing a BLOB in the local file system, in a shared database or on the [Azure BLOB storage](https://azure.microsoft.com/en-us/services/storage/blobs/) can be an option.
@ -10,7 +10,9 @@ ABP Framework provides an abstraction to work with BLOBs and provides some pre-b
* You can then **easily change** your BLOB storage without changing your application code. * You can then **easily change** your BLOB storage without changing your application code.
* If you want to create **reusable application modules**, you don't need to make assumption about how the BLOBs are stored. * If you want to create **reusable application modules**, you don't need to make assumption about how the BLOBs are stored.
### BLOB Storage Providers ABP BLOB Storage system is also compatible to other ABP Framework features like [multi-tenancy](Multi-Tenancy.md).
## BLOB Storage Providers
ABP Framework has the following storage provider implementations; ABP Framework has the following storage provider implementations;
@ -22,7 +24,7 @@ More providers will be implemented by the time. You can [request](https://github
Multiple providers **can be used together** by the help of the **container system**, where each container can use a different provider (will be explained below). Multiple providers **can be used together** by the help of the **container system**, where each container can use a different provider (will be explained below).
> BLOB storing system can not work unless you **configure a storage provider**. Refer to the linked documents for the storage providers. > BLOB storing system can not work unless you **configure a storage provider**. Refer to the linked documents for the storage provider configurations.
## Volo.Abp.BlobStoring Package ## Volo.Abp.BlobStoring Package
@ -65,7 +67,7 @@ namespace AbpDemo
await _blobContainer.SaveAsync("my-blob-1", bytes); await _blobContainer.SaveAsync("my-blob-1", bytes);
} }
public async Task<byte[]> GetBytesAsync(byte[] bytes) public async Task<byte[]> GetBytesAsync()
{ {
return await _blobContainer.GetAllBytesOrNullAsync("my-blob-1"); return await _blobContainer.GetAllBytesOrNullAsync("my-blob-1");
} }
@ -77,16 +79,227 @@ This service saves given bytes with the `my-blob-1` name and then reads the prev
> A BLOB is a named object and **each BLOB should have a unique name**, which is an arbitrary string. > A BLOB is a named object and **each BLOB should have a unique name**, which is an arbitrary string.
`IBlobContainer` can work with `Stream` and `byte[]` objects, which will be detailed in the next sections.
#### Saving BLOBs #### Saving BLOBs
TODO `SaveAsync` method is used to save a new BLOB or replace an existing BLOB. It can save a `Stream` by default, but there is a shortcut extension method to save byte arrays.
`SaveAsync` gets the following parameters:
* **name** (string): Unique name of the BLOB.
* **stream** (Stream) or **bytes** (byte[]): The stream to read the BLOB content or a byte array.
* **overrideExisting** (bool): Set `true` to replace the BLOB content if it does already exists. Default value is `false` and throws `BlobAlreadyExistsException` if there is already a BLOB in the container with the same name.
#### Reading/Getting BLOBs #### Reading/Getting BLOBs
* `GetAsync`: Only gets a BLOB name and returns a `Stream` object that can be used to read the BLOB content. Always **dispose the stream** after using it. This method throws exception if can not find the BLOB with the given name.
* `GetOrNullAsync`: In opposite to the `GetAsync` method, this one returns `null` if there is no BLOB found with the given name.
* `GetAllBytesAsync`: Returns a `byte[]` instead of a `Stream`. Still throws exception if can not find the BLOB with the given name.
* `GetAllBytesOrNullAsync`: In opposite to the `GetAllBytesAsync` method, this one returns `null` if there is no BLOB found with the given name.
#### Deleting BLOBs #### Deleting BLOBs
`DeleteAsync` method gets a BLOB name and deletes the BLOB data. It doesn't throw any exception if given BLOB was not found, instead it returns a `bool` indicates that the BLOB was actually deleted or not.
#### Other Methods #### Other Methods
## Implementing Your Own BLOB Storage Provider * `ExistsAsync` method simply checks if there is a saved BLOB with the given name.
### Typed IBlobContainer
Typed BLOB container is a way of creating and managing **multiple containers** in an application;
* **Each container is separately stored**. That means BLOB names should be unique in a container and two BLOBs with the same name can live in different containers without effecting each other.
* **Each container can be separately configured**, so each container can use a different storage provider based on your configuration.
To create a typed container, you need to create a simple class decorated with the `BlobContainerName` attribute:
````csharp
using Volo.Abp.BlobStoring;
namespace AbpDemo
{
[BlobContainerName("profile-pictures")]
public class ProfilePictureContainer
{
}
}
````
> If you don't use the `BlobContainerName` attribute, ABP Framework uses the full name of the class (with namespace), but it is always recommended to use a container name which is not changed even if you rename the class.
Once you create the container class, you can inject `IBlobContainer<T>` for your container type.
**Example: An [application service](Application-Services.md) to save and read profile picture of the current user**
````csharp
[Authorize]
public class ProfileAppService : ApplicationService
{
private readonly IBlobContainer<ProfilePictureContainer> _blobContainer;
public ProfileAppService(IBlobContainer<ProfilePictureContainer> blobContainer)
{
_blobContainer = blobContainer;
}
public async Task SaveProfilePictureAsync(byte[] bytes)
{
var blobName = CurrentUser.GetId().ToString();
await _blobContainer.SaveAsync(blobName, bytes);
}
public async Task<byte[]> GetProfilePictureAsync()
{
var blobName = CurrentUser.GetId().ToString();
return await _blobContainer.GetAllBytesOrNullAsync(blobName);
}
}
````
`IBlobContainer<T>` has the same methods with the `IBlobContainer`.
> It is a good practice to **always use a typed container while developing re-usable modules**, so the final application can configure the provider for your container without effecting the other containers.
#### The Default Container
If you don't use the generic argument and directly inject the `IBlobContainer` (as explained before), you get the default container. Another way of injecting the default container is like `IBlobContainer<DefaultContainer>`, which returns exactly the same container.
The name of the default container is `Default`.
#### Named Containers
Actually, types containers are just a shortcut for named containers. You can inject and use the `IBlobContainerFactory` to get a BLOB container by its name:
````csharp
public class ProfileAppService : ApplicationService
{
private readonly IBlobContainer _blobContainer;
public ProfileAppService(IBlobContainerFactory blobContainerFactory)
{
_blobContainer = blobContainerFactory.Create("profile-pictures");
}
//...
}
````
### IBlobContainerFactory
`IBlobContainerFactory` is the service that is used to create the BLOB containers. One example was shown above.
**Example: Create a container by name**
````csharp
var blobContainer = blobContainerFactory.Create("profile-pictures");
````
**Example: Create a container by type**
````csharp
var blobContainer = blobContainerFactory.Create<ProfilePictureContainer>();
````
> You generally don't need to use the `IBlobContainerFactory` since it is used internally when you inject a `IBlobContainer` or `IBlobContainer<T>`.
### About Naming the BLOBs
Naming BLOBs has not a forcing rule. A BLOB name is just a string that is unique per container. However, different storage providers may conventionally implement some practices. For example, the [File System Provider](Blob-Storing-File-System.md) use directory separators (`/`) and file extensions in your BLOB name (if your BLOB name is `images/common/x.png` then it is saved as `x.png` in the `images/common` folder of the root container folder).
## Configuring the Containers
Containers should be configured before using them. The most fundamental configuration is to **select a BLOB storage provider** (see the "*BLOB Storage Providers*" section above).
`AbpBlobStoringOptions` is the [options class](Options.md) to configure the containers. You can configure the options inside the `ConfigureServices` method of your [module](Module-Development-Basics.md).
### Configure a Single Container
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.Configure<ProfilePictureContainer>(container =>
{
//TODO...
});
});
````
This example configures the `ProfilePictureContainer`. You can also configure by the container name:
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.Configure("profile-pictures", container =>
{
//TODO...
});
});
````
### Configure the Default Container
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
//TODO...
});
});
````
> There is a special case about the default container; If you don't specify a configuration for a container, it **fallbacks to the default container configuration**. This is a good way to configure defaults for all containers and specialize configuration for a specific container when needed.
### Configure All Containers
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureAll((containerName, containerConfiguration) =>
{
//TODO...
});
});
````
This is a way to configure all the containers. The main difference from configuring the default container is that you override the configuration even if it was specialized for a specific container.
## Multi-Tenancy
ABP BLOB Storing system **works silently with the [multi-tenancy](Multi-Tenancy.md)** if your application is multi-tenant. All the providers implement multi-tenancy as a standard feature. They **isolate BLOBs** of different tenants from each other, so they can only access to their own BLOBs. That also means you can use the **same BLOB name** for different tenants.
If your application is multi-tenant, you may want to control **multi-tenancy behavior** of the containers individually. For example, you may want to **disable multi-tenancy** for a specific container, so the BLOBs inside it will be **available to all the tenants**. This is a way to share BLOBs among tenants.
**Example: Disable multi-tenancy for a specific container**
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.Configure<ProfilePictureContainer>(container =>
{
container.IsMultiTenant = false;
});
});
````
> If your application is not multi-tenant, no worry, it works as expected. You don't need to configure anything.
## Extending the BLOB Storing System
Most of the times, you won't need to customize the BLOB storage system except [creating a custom BLOB storage provider](Blob-Storing-Custom-Provider.md). However, you can replace any service introduced above (via [dependency injection](Dependency-Injection.md)), if you need. Here, some other services not mentioned above, but you may want to know:
* `IBlobProviderSelector` is used to get a `IBlobProvider` instance by a container name. Default implementation (`DefaultBlobProviderSelector`) selects the provider using the configuration.
* `IBlobContainerConfigurationProvider` is used to get the `BlobContainerConfiguration` for a given container name. Default implementation (`DefaultBlobContainerConfigurationProvider`) gets the configuration from the `AbpBlobStoringOptions` explained above.
## BLOB Storing vs File Management System
Notice that BLOB storing is not a file management system. It is used to save, get and delete named BLOBs. It doesn't provide a hierarchical structure like directories, you may expect from a typical file system. BLOB Storing is a low level system.
If you want to create folders and move files between folders, assign permissions to files and share files between users then you need to implement your own application on top of the BLOB Storing system.
## See Also
TODO * [Creating a custom BLOB storage provider](Blob-Storing-Custom-Provider.md)

30
docs/en/docs-nav.json

@ -179,6 +179,36 @@
"text": "Object to object mapping", "text": "Object to object mapping",
"path": "Object-To-Object-Mapping.md" "path": "Object-To-Object-Mapping.md"
}, },
{
"text": "BLOB Storing",
"items": [
{
"text": "BLOB Storing System",
"path": "Blob-Storing.md"
},
{
"text": "Storage Providers",
"items": [
{
"text": "File System Provider",
"path": "Blob-Storing-File-System.md"
},
{
"text": "Database Provider",
"path": "Blob-Storing-Database.md"
},
{
"text": "Azure Provider",
"path": "Blob-Storing-Azure.md"
},
{
"text": "Create a Custom Provider",
"path": "Blob-Storing-Custom-Provider.md"
}
]
}
]
},
{ {
"text": "Text Templating", "text": "Text Templating",
"path": "Text-Templating.md" "path": "Text-Templating.md"

5
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactory.cs

@ -1,5 +1,4 @@
using System.Threading; using Volo.Abp.DependencyInjection;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
@ -27,7 +26,7 @@ namespace Volo.Abp.BlobStoring
ProviderSelector = providerSelector; ProviderSelector = providerSelector;
} }
public virtual IBlobContainer Create(string name, CancellationToken cancellationToken = default) public virtual IBlobContainer Create(string name)
{ {
var configuration = ConfigurationProvider.Get(name); var configuration = ConfigurationProvider.Get(name);

10
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/BlobContainerFactoryExtensions.cs

@ -1,6 +1,4 @@
using System.Threading; namespace Volo.Abp.BlobStoring
namespace Volo.Abp.BlobStoring
{ {
public static class BlobContainerFactoryExtensions public static class BlobContainerFactoryExtensions
{ {
@ -13,13 +11,11 @@ namespace Volo.Abp.BlobStoring
/// The container object. /// The container object.
/// </returns> /// </returns>
public static IBlobContainer Create<TContainer>( public static IBlobContainer Create<TContainer>(
this IBlobContainerFactory blobContainerFactory, this IBlobContainerFactory blobContainerFactory
CancellationToken cancellationToken = default
) )
{ {
return blobContainerFactory.Create( return blobContainerFactory.Create(
BlobContainerNameAttribute.GetContainerName<TContainer>(), BlobContainerNameAttribute.GetContainerName<TContainer>()
cancellationToken
); );
} }
} }

4
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/DefaultContainer.cs

@ -1,8 +1,8 @@
namespace Volo.Abp.BlobStoring namespace Volo.Abp.BlobStoring
{ {
[BlobContainerName("Default")] [BlobContainerName(Name)]
public class DefaultContainer public class DefaultContainer
{ {
public const string Name = "Default";
} }
} }

5
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerConfigurationProvider.cs

@ -2,6 +2,11 @@
{ {
public interface IBlobContainerConfigurationProvider public interface IBlobContainerConfigurationProvider
{ {
/// <summary>
/// Gets a <see cref="BlobContainerConfiguration"/> for the given container <paramref name="name"/>.
/// </summary>
/// <param name="name">The name of the container</param>
/// <returns>The configuration that should be used for the container</returns>
BlobContainerConfiguration Get(string name); BlobContainerConfiguration Get(string name);
} }
} }

8
framework/src/Volo.Abp.BlobStoring/Volo/Abp/BlobStoring/IBlobContainerFactory.cs

@ -1,6 +1,4 @@
using System.Threading; namespace Volo.Abp.BlobStoring
namespace Volo.Abp.BlobStoring
{ {
public interface IBlobContainerFactory public interface IBlobContainerFactory
{ {
@ -8,13 +6,11 @@ namespace Volo.Abp.BlobStoring
/// Gets a named container. /// Gets a named container.
/// </summary> /// </summary>
/// <param name="name">The name of the container</param> /// <param name="name">The name of the container</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns> /// <returns>
/// The container object. /// The container object.
/// </returns> /// </returns>
IBlobContainer Create( IBlobContainer Create(
string name, string name
CancellationToken cancellationToken = default
); );
} }
} }
Loading…
Cancel
Save