项目

BLOB存储

在应用中存储文件内容并按需读取是常见需求。除了文件,您可能还需要将各类大型二进制对象(即BLOB)保存至存储系统。例如,保存用户头像图片。

BLOB本质上是字节数组。存储BLOB项目有多种选择:本地文件系统、共享数据库或Azure BLOB存储等。

ABP框架提供了处理BLOB的抽象层,并内置了多种可轻松集成的存储提供程序。这种抽象设计具有以下优势:

  • 通过几行配置即可轻松集成您偏好的BLOB存储服务。
  • 后续可轻松更换BLOB存储方案,而无需修改应用代码。
  • 开发可复用应用模块时,无需对BLOB存储方式做任何假设。

ABP BLOB存储系统还兼容其他ABP特性,如 多租户支持

BLOB存储提供程序

ABP已内置以下存储提供程序实现:

未来将持续增加更多提供程序。您可 提交请求 添加心仪的提供程序,或 自行创建贡献 给 ABP 项目。

通过容器系统支持多提供程序协同工作,每个容器可配置使用不同的提供程序。

必须配置存储提供程序后BLOB存储系统才能正常工作。请参阅各提供程序文档了解配置细节。

安装

Volo.Abp.BlobStoring是定义BLOB存储服务的核心包。使用此包可接入BLOB存储系统而不依赖特定存储提供程序。

使用ABP CLI添加此包至项目:

  • 若未安装,请先安装 ABP CLI
  • 在目标.csproj文件所在目录打开命令行(终端)。
  • 运行abp add-package Volo.Abp.BlobStoring命令。

手动安装时,将Volo.Abp.BlobStoring NuGet包添加至项目,并在项目内的ABP模块类中添加[DependsOn(typeof(AbpBlobStoringModule))]

IBlobContainer接口

IBlobContainer是存储和读取BLOB的核心接口。应用可包含多个容器,每个容器可独立配置。通过依赖注入IBlobContainer即可使用默认容器

示例:简单保存和读取指定名称的BLOB字节数据

using System.Threading.Tasks;
using Volo.Abp.BlobStoring;
using Volo.Abp.DependencyInjection;

namespace AbpDemo
{
    public class MyService : ITransientDependency
    {
        private readonly IBlobContainer _blobContainer;

        public MyService(IBlobContainer blobContainer)
        {
            _blobContainer = blobContainer;
        }

        public async Task SaveBytesAsync(byte[] bytes)
        {
            await _blobContainer.SaveAsync("my-blob-1", bytes);
        }
        
        public async Task<byte[]> GetBytesAsync()
        {
            return await _blobContainer.GetAllBytesOrNullAsync("my-blob-1");
        }
    }
}

此服务将给定字节以my-blob-1名称保存,随后读取同名BLOB的字节数据。

BLOB是命名对象,每个BLOB需具备唯一名称(任意字符串)。

IBlobContainer支持处理Streambyte[]对象,后续章节将详细说明。

保存BLOB

SaveAsync方法用于保存新BLOB或替换现有BLOB。默认支持保存Stream对象,同时提供保存字节数组的快捷扩展方法。

SaveAsync参数说明:

  • name (string):BLOB的唯一名称。
  • stream (Stream) 或 bytes (byte[]):读取BLOB内容的流或字节数组。
  • overrideExisting (bool):设为true可替换已存在的BLOB内容。默认值为false,若容器中已存在同名BLOB将抛出BlobAlreadyExistsException

读取/获取BLOB

  • GetAsync:仅接收BLOB名称,返回用于读取BLOB内容的Stream对象。使用后务必释放流。若未找到指定名称的BLOB则抛出异常。
  • GetOrNullAsync:与GetAsync相反,未找到BLOB时返回null
  • GetAllBytesAsync:返回byte[]而非Stream。未找到BLOB时仍抛出异常。
  • GetAllBytesOrNullAsync:与GetAllBytesAsync相反,未找到BLOB时返回null

删除BLOB

DeleteAsync方法接收BLOB名称并删除对应数据。未找到BLOB时不抛出异常,而是返回bool指示是否实际执行了删除操作(如需关注此结果)。

其他方法

  • ExistsAsync方法简单检查容器中是否存在指定名称的BLOB。

关于BLOB命名

BLOB命名无硬性规则。BLOB名称仅是每容器(及每租户——参见“多租户”章节)内唯一的字符串。但不同存储提供程序可能遵循某些惯例。例如文件系统提供程序会在BLOB名称中使用目录分隔符(/)和文件扩展名(若BLOB名称为images/common/x.png,则实际保存路径为根容器文件夹内的images/common/x.png)。

类型化IBlobContainer

类型化BLOB容器系统用于在应用中创建和管理多个容器

  • 每个容器独立存储。即BLOB名称在容器内唯一,不同容器中可存在同名BLOB且互不影响。
  • 每个容器可独立配置,因此可根据配置为各容器选用不同存储提供程序。

创建类型化容器需定义带有BlobContainerName特性的简单类:

using Volo.Abp.BlobStoring;

namespace AbpDemo
{
    [BlobContainerName("profile-pictures")]
    public class ProfilePictureContainer
    {
        
    }
}

若不使用BlobContainerName特性,ABP将使用类的完整名称(含命名空间),但建议采用稳定的容器名称(即使重命名类也不会变化)。

创建容器类后,可注入对应容器类型的IBlobContainer<T>

示例:应用服务保存和读取当前用户头像

[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>方法与IBlobContainer完全相同。

开发可复用模块时始终使用类型化容器是最佳实践,这样最终应用可为您的容器配置提供程序而不影响其他容器。

默认容器

若不使用泛型参数直接注入IBlobContainer(如前所述),将获得默认容器。另一种注入默认容器的方式是使用IBlobContainer<DefaultContainer>,两者完全等效。

默认容器名称为default

命名容器

类型化容器仅是命名容器的快捷方式。可通过注入使用IBlobContainerFactory按名称获取BLOB容器:

public class ProfileAppService : ApplicationService
{
    private readonly IBlobContainer _blobContainer;

    public ProfileAppService(IBlobContainerFactory blobContainerFactory)
    {
        _blobContainer = blobContainerFactory.Create("profile-pictures");
    }

    //...
}

IBlobContainerFactory

IBlobContainerFactory是用于创建BLOB容器的服务。上文已展示示例。

示例:按名称创建容器

var blobContainer = blobContainerFactory.Create("profile-pictures");

示例:按类型创建容器

var blobContainer = blobContainerFactory.Create<ProfilePictureContainer>();

通常无需直接使用IBlobContainerFactory,因其在注入IBlobContainerIBlobContainer<T>时内部自动调用。

配置容器

使用容器前需进行配置。最基础的配置是选择BLOB存储提供程序(参见前文“BLOB存储提供程序”章节)。

AbpBlobStoringOptions是配置容器的选项类。可在模块的ConfigureServices方法中配置选项。

配置单个容器

Configure<AbpBlobStoringOptions>(options =>
{
    options.Containers.Configure<ProfilePictureContainer>(container =>
    {
        //配置内容...
    });
});

此示例配置ProfilePictureContainer。也可按容器名称配置:

Configure<AbpBlobStoringOptions>(options =>
{
    options.Containers.Configure("profile-pictures", container =>
    {
        //配置内容...
    });
});

配置默认容器

Configure<AbpBlobStoringOptions>(options =>
{
    options.Containers.ConfigureDefault(container =>
    {
        //配置内容...
    });
});

默认容器有特殊逻辑:若未为某容器指定配置,将回退至默认容器配置。这是为所有容器设置默认值,并在需要时为特定容器定制配置的有效方式。

配置所有容器

Configure<AbpBlobStoringOptions>(options =>
{
    options.Containers.ConfigureAll((containerName, containerConfiguration) =>
    {
        //配置内容...
    });
});

此方式可一次性配置所有容器。

与配置默认容器的主要区别在于:ConfigureAll会覆盖已为特定容器设置的专项配置。

多租户支持

若应用设置为多租户模式,BLOB存储系统可与 多租户 无缝协作。所有提供程序均将多租户作为标准功能实现。它们隔离不同租户的BLOB,确保各租户仅能访问自身BLOB。这意味着不同租户可使用相同BLOB名称

对于多租户应用,您可能需要单独控制各容器的多租户行为。例如,为特定容器禁用多租户,使其中的BLOB对所有租户可见。这是实现租户间共享BLOB的途径。

示例:为特定容器禁用多租户

Configure<AbpBlobStoringOptions>(options =>
{
    options.Containers.Configure<ProfilePictureContainer>(container =>
    {
        container.IsMultiTenant = false;
    });
});

若应用非多租户,无需担忧,系统将按预期工作。不必配置IsMultiTenant选项。

扩展BLOB存储系统

创建自定义BLOB存储提供程序外,通常无需定制BLOB存储系统。但必要时可通过依赖注入替换任何服务。以下是一些未提及但可能需了解的服务:

  • IBlobProviderSelector用于按容器名称获取IBlobProvider实例。默认实现(DefaultBlobProviderSelector)根据配置选择提供程序。
  • IBlobContainerConfigurationProvider用于获取指定容器名称的BlobContainerConfiguration。默认实现(DefaultBlobContainerConfigurationProvider)从上述AbpBlobStoringOptions获取配置。

BLOB存储 vs 文件管理系统

请注意BLOB存储并非文件管理系统。它是用于保存、获取和删除命名BLOB的低层系统。不提供典型文件系统中的层次化结构(如目录)。

若需创建文件夹、在文件夹间移动文件、为文件分配权限及用户间共享文件,需在BLOB存储系统之上自行实现应用逻辑。

另请参阅

在本文档中