File System Storage Provider is used to store BLOBs in the local file system as standard files inside a folder.
> Read the BLOB Storing document to understand how to use the BLOB storing system. This document only covers how to configure containers to use the file system.
## Installation
Use the ABP CLI to add [Volo.Abp.BlobStoring.FileSystem](https://www.nuget.org/packages/Volo.Abp.BlobStoring.FileSystem) NuGet package to your project:
* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI) if you haven't installed before.
* Open a command line (terminal) in the directory of the `.csproj` file you want to add the `Volo.Abp.BlobStoring.FileSystem` package.
* Run `abp add-package Volo.Abp.BlobStoring.FileSystem` command.
If you want to do it manually, install the [Volo.Abp.BlobStoring.FileSystem](https://www.nuget.org/packages/Volo.Abp.BlobStoring.FileSystem) NuGet package to your project and add `[DependsOn(typeof(AbpBlobStoringFileSystemModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project.
## Configuration
Configuration is done in the `ConfigureServices` method of your [module](Module-Development-Basics.md) class, as explained in the [BLOB Storing document](Blob-Storing.md).
**Example: Configure to use the File System storage provider by default**
````csharp
Configure<AbpBlobStoringOptions>(options =>
{
options.Containers.ConfigureDefault(container =>
{
container.UseFileSystem(fileSystem =>
{
fileSystem.BasePath = "C:\\my-files";
});
});
});
````
`UseFileSystem` extension method is used to set the File System Provider for a container and configure the file system options.
> See the [BLOB Storing document](Blob-Storing.md) to learn how to configure this provider for a specific container.
### Options
* **BasePath** (string): The base folder path to store BLOBs. It is required to set this option.
* **AppendContainerNameToBasePath** (bool; default: `true`): Indicates whether to create a folder with the container name inside the base folder. If you store multiple containers in the same `BaseFolder`, leave this as `true`. Otherwise, you can set it to `false` if you don't like an unnecessarily deeper folder hierarchy.
## File Path Calculation
File System Provider organizes BLOB files inside folders and implements some conventions. The full path of a BLOB file is determined by the following rules by default:
* It starts with the `BasePath` configured as shown above.
* Appends `host` folder if [current tenant](Multi-Tenancy.md) is `null` (or multi-tenancy is disabled for the container - see the [BLOB Storing document](Blob-Storing.md) to learn how to disable multi-tenancy for a container).
* Appends `tenants/<tenant-id>` folder if current tenant is not `null`.
* Appends the container's name if `AppendContainerNameToBasePath` is `true`. If container name contains `/`, this will result with nested folders.
* Appends the BLOB name. If the BLOB name contains `/` it creates folders. If the BLOB name contains `.` it will have a file extension.
## Extending the File System BLOB Provider
* `FileSystemBlobProvider` is the main service that implements the File System storage. You can inherit from this class and [override](Customizing-Application-Modules-Overriding-Services.md) methods to customize it.
* The `IBlobFilePathCalculator` service is used to calculate the file paths. Default implementation is the `DefaultBlobFilePathCalculator`. You can replace/override it if you want to customize the file path calculation.
It is typical to **store file contents** in an application and read these file contents whenever we need. Not only the 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 can save the 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 a typically **byte array**. There are various places to store a BLOB item; storing 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 options.
@ -20,18 +20,16 @@ The ABP Framework has already the following storage provider implementations;
* [Database](Blob-Storing-Database.md): Stores BLOBs in a database.
* [Azure](Blob-Storing-Azure.md): Stores BLOBs on the [Azure BLOB storage](https://azure.microsoft.com/en-us/services/storage/blobs/).
More providers will be implemented by the time. You can [request](https://github.com/abpframework/abp/issues/new) it for your favorite provider or [contribute](Contribution/Index.md) it yourself (see the "*Implementing Your Own BLOB Storage Provider*" section).
More providers will be implemented by the time. You can [request](https://github.com/abpframework/abp/issues/new) it for your favorite provider or [create it yourself](Blob-Storing-Custom-Provider.md).
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 provider configurations.
## Volo.Abp.BlobStoring Package
## Installation
[Volo.Abp.BlobStoring](https://www.nuget.org/packages/Volo.Abp.BlobStoring) is the main package that defines the BLOB storing services. You can use this package to use the BLOB Storing system without depending a specific storage provider.
### Installation
Use the ABP CLI to add this package to your project:
* Install the [ABP CLI](https://docs.abp.io/en/abp/latest/CLI), if you haven't installed it.
@ -40,7 +38,7 @@ Use the ABP CLI to add this package to your project:
If you want to do it manually, install the [Volo.Abp.BlobStoring](https://www.nuget.org/packages/Volo.Abp.BlobStoring) NuGet package to your project and add `[DependsOn(typeof(AbpBlobStoringModule))]` to the [ABP module](Module-Development-Basics.md) class inside your project.
### The IBlobContainer
## The IBlobContainer
`IBlobContainer` is the main interface to store and read BLOBs. Your application may have multiple containers and each container can be separately configured. But, there is a **default container** that can be simply used by [injecting](Dependency-Injection.md) the `IBlobContainer`.
@ -81,7 +79,7 @@ This service saves the given bytes with the `my-blob-1` name and then gets the p
`IBlobContainer` can work with `Stream` and `byte[]` objects, which will be detailed in the next sections.
#### Saving BLOBs
### Saving BLOBs
`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.
@ -89,25 +87,30 @@ This service saves the given bytes with the `my-blob-1` name and then gets the p
* **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 already exists. The default value is `false`. If the container has a BLOB with the same name and you do not set `overrideExisting = true`, it throws `BlobAlreadyExistsException`.
#### Reading/Getting BLOBs
* **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
* `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 it 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` indicating that the BLOB was actually deleted or not, if you care about it.
`DeleteAsync` method gets a BLOB name and deletes the BLOB data. It doesn't throw any exception if the given BLOB was not found, instead it returns a `bool` indicates that the BLOB was actually deleted or not.
### Other Methods
#### Other Methods
* `ExistsAsync` method simply checks if there is a BLOB in the container with the given name.
### About Naming the BLOBs
* `ExistsAsync` method simply checks if there is a saved BLOB with the given name.
There is not a rule for naming the BLOBs. A BLOB name is just a string that is unique per container (and per tenant - see the "*Multi-Tenancy*" section). 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 inside the root container folder).
### Typed IBlobContainer
## Typed IBlobContainer
Typed BLOB container is a way of creating and managing **multiple containers** in an application;
Typed BLOB container system is a way of creating and managing **multiple containers** in an application;
* **Each container is separately stored**. That means the 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.
@ -131,7 +134,7 @@ namespace AbpDemo
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**
**Example: An [application service](Application-Services.md) to save and read profile picture of the [current user](CurrentUser.md)**
````csharp
[Authorize]
@ -162,13 +165,13 @@ public class ProfileAppService : ApplicationService
> 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
### 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 using `IBlobContainer<DefaultContainer>`, which returns exactly the same container.
The name of the default container is `Default`.
#### Named Containers
### Named Containers
Typed containers are just shortcuts for named containers. You can inject and use the `IBlobContainerFactory` to get a BLOB container by its name:
@ -186,7 +189,7 @@ public class ProfileAppService : ApplicationService
}
````
### IBlobContainerFactory
## IBlobContainerFactory
`IBlobContainerFactory` is the service that is used to create the BLOB containers. One example was shown above.
@ -204,10 +207,6 @@ 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 is not a compelling 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. For example, 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).
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.
This is a way to configure all the containers.
> The main difference from configuring the default container is that `ConfigureAll` overrides the configuration even if it was specialized for a specific container.
## Multi-Tenancy
If your application is set as multi-tenant, the BLOB Storage system **works seamlessly with the [multi-tenancy](Multi-Tenancy.md)**. 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. It 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, this way the BLOB items will be **available to all the tenants**. This is a way to share the BLOB items among 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 all tenants.
**Example: Disable multi-tenancy for a specific container**
> No worries, if your application is not multi-tenant, it works as expected. You don't need to configure anything.
> If your application is not multi-tenant, no worry, it works as expected. You don't need to configure the `IsMultiTenant` option.
## 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 are some other services related to the BLOB Storage 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 (injected 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.
Notice that BLOB storing is not a file management system. It is a low level system that 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.
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 Storage system.