什么是对象存储
在工作中,咱们常常须要将文件内容(文件或二进制流)存储在应用程序中,例如你可能要保留商品的封面图片。Masa 框架为此提供了对象存储的性能,并对性能形象,形象给咱们带来的益处:
- 存储的无关性(不关怀存储平台时阿里云 OSS 还是腾讯云的 COS)
- 更换存储平台老本更低(仅须要更改下存储的提供者,业务侵染低)
- 反对自定义存储提供者(仅须要自行实现
IClient
)
对象存储提供程序
- 阿里云: 在阿里云 OSS 存储服务上存储
目前仅反对阿里云存储,后续将逐渐提供更多的云存储平台反对,如果您有喜爱的其它云存储平台,欢送提倡议,或者本人实现它并为 Masa 框架做出奉献
如何制作自定义存储程序?
疾速入门
Masa.BuildingBlocks.Storage.ObjectStorage 是对象存储服务的形象包,你能够在我的项目中应用它来进行编写代码,最初在 Program.cs
中抉择一个存储提供程序应用即可
- 装置.Net 6.0
-
新建 ASP.NET Core 空我的项目
Assignment.OSS
,并装置Masa.Contrib.Storage.ObjectStorage.Aliyun
dotnet new web -o Assignment.OSS cd Assignment.OSS dotnet add package Masa.Contrib.Storage.ObjectStorage.Aliyun --version 0.5.0-preview.2
-
批改
Program.cs
builder.Services.AddAliyunStorage(); #region 或者通过代码指定传入阿里云存储配置信息应用,无需应用配置文件 // builder.Services.AddAliyunStorage(new AliyunStorageOptions() // { // AccessKeyId = "Replace-With-Your-AccessKeyId", // AccessKeySecret = "Replace-With-Your-AccessKeySecret", // Endpoint = "Replace-With-Your-Endpoint", // RoleArn = "Replace-With-Your-RoleArn", // RoleSessionName = "Replace-With-Your-RoleSessionName", // Sts = new AliyunStsOptions() // { // RegionId = "Replace-With-Your-Sts-RegionId", // DurationSeconds = 3600, // EarlyExpires = 10 // } // }, "storage1-test"); #endregion
-
批改
appsettings.json
,减少阿里云配置{ "Aliyun": { "AccessKeyId": "Replace-With-Your-AccessKeyId", "AccessKeySecret": "Replace-With-Your-AccessKeySecret", "Sts": { "RegionId": "Replace-With-Your-Sts-RegionId", "DurationSeconds": 3600, "EarlyExpires": 10 }, "Storage": { "Endpoint": "Replace-With-Your-Endpoint", "RoleArn": "Replace-With-Your-RoleArn", "RoleSessionName": "Replace-With-Your-RoleSessionName", "TemporaryCredentialsCacheKey": "Aliyun.Storage.TemporaryCredentials", "Policy": "","BucketNames": {"DefaultBucketName":"storage1-test"// 默认 BucketName,非必填项,仅在应用 IClientContainer 时须要指定} } } }
-
新增上传文件服务
app.MapPost("/upload", async (HttpRequest request, IClient client) => {var form = await request.ReadFormAsync(); var formFile = form.Files["file"]; if (formFile == null) throw new FileNotFoundException("Can't upload empty file"); await client.PutObjectAsync("storage1-test", formFile.FileName, formFile.OpenReadStream()); });
进阶
IClient
IClient
是用来存储和读取对象的次要接口,能够在我的项目的任意中央通过 DI 获取到 IClient
来上传、下载或删除指定 BucketName
下的对象,也可用于判断对象是否存在,获取长期凭证等。
-
上传对象
app.MapPost("/upload", async (HttpRequest request, IClient client) => {var form = await request.ReadFormAsync(); var formFile = form.Files["file"]; if (formFile == null) throw new FileNotFoundException("Can't upload empty file"); await client.PutObjectAsync("storage1-test", formFile.FileName, formFile.OpenReadStream()); });
Form 表单提交,key 为 file,类型为文件上传
-
删除对象
public class DeleteRequest {public string Key { get; set;} } app.MapDelete("/delete", async (IClient client, [FromBody] DeleteRequest request) => {await client.DeleteObjectAsync("storage1-test", request.Key); });
-
判断对象是否存在
app.MapGet("/exist", async (IClient client, string key) => {await client.ObjectExistsAsync("storage1-test", key); });
-
返回对象数据的流
app.MapGet("/download", async (IClient client, string key, string path) => { await client.GetObjectAsync("storage1-test", key, stream => { // 下载文件到指定门路 using var requestStream = stream; byte[] buf = new byte[1024]; var fs = File.Open(path, FileMode.OpenOrCreate); int len; while ((len = requestStream.Read(buf, 0, 1024)) != 0) {fs.Write(buf, 0, len); } fs.Close();}); });
-
获取长期凭证(STS)
app.MapGet("/GetSts", (IClient client) => {client.GetSecurityToken(); });
阿里云、腾讯云存储等平台应用 STS 来获取长期凭证
-
获取长期凭证(字符串类型的长期凭证)
app.MapGet("/GetToken", (IClient client) => {client.GetToken(); });
七牛云等存储平台应用较多
IBucketNameProvider
IBucketNameProvider
是用来获取 BucketName 的接口,通过 IBucketNameProvider
能够获取指定存储空间的 BucketName,为 IClientContainer
提供 BucketName 能力,在业务我的项目中不会应用到
IClientContainer
IClientContainer
对象存储容器,用来存储和读取对象的次要接口,一个应用程序下可能会存在治理多个 BucketName,通过应用 IClientContainer
,像治理DbContext
一样治理不同 Bucket
的对象,不须要在我的项目中频繁指定 BucketName
,在同一个应用程序中,有且只有一个默认 ClientContainer,能够通过 DI 获取IClientContainer
来应用,例如:
-
上传对象(上传到默认
Bucket
)app.MapPost("/upload", async (HttpRequest request, IClientContainer clientContainer) => {var form = await request.ReadFormAsync(); var formFile = form.Files["file"]; if (formFile == null) throw new FileNotFoundException("Can't upload empty file"); await clientContainer.PutObjectAsync(formFile.FileName, formFile.OpenReadStream()); });
-
上传到指定
Bucket
[BucketName("picture")] public class PictureContainer { } builder.Services.Configure<StorageOptions>(option => {option.BucketNames = new BucketNames(new List<KeyValuePair<string, string>>() {new("DefaultBucketName", "storage1-test"),// 默认 BucketName new("picture", "storage1-picture")// 指定别名为 picture 的 BucketName 为 storage1-picture }); }); app.MapPost("/upload", async (HttpRequest request, IClientContainer<PictureContainer> clientContainer) => {var form = await request.ReadFormAsync(); var formFile = form.Files["file"]; if (formFile == null) throw new FileNotFoundException("Can't upload empty file"); await clientContainer.PutObjectAsync(formFile.FileName, formFile.OpenReadStream()); });
IClientFactory
IClientFactory
对象存储提供者工厂,通过指定BucketName
,创立指定的 IClientContainer
创建对象存储提供程序
以适配腾讯云存储为例:
- 新建类库
Masa.Contrib.Storage.ObjectStorage.Tencent
- 选中
Masa.Contrib.Storage.ObjectStorage.Tencent
并新建类DefaultStorageClient
,并实现IClient
- 因为腾讯云存储提供 Sts 长期凭证,所以仅须要实现
GetSecurityToken
办法即可,GetToken
办法可抛出不反对的异样,并在文档阐明即可 -
新建类
ServiceCollectionExtensions
,并提供对IServiceCollection
的扩大办法AddTencentStorage
,例如:public static IServiceCollection AddTencentStorage( this IServiceCollection services, TencentStorageOptions options, string? defaultBucketName = null) { //todo: 增加腾讯云存储的客户端 if (defaultBucketName != null) { services.Configure<StorageOptions>(option => {option.BucketNames = new BucketNames(new List<KeyValuePair<string, string>>() {new(BucketNames.DEFAULT_BUCKET_NAME, defaultBucketName) }); }); services.TryAddSingleton<IClientContainer>(serviceProvider => new DefaultClientContainer(serviceProvider.GetRequiredService<IClient>(), defaultBucketName)); } services.TryAddSingleton<IClientFactory, DefaultClientFactory>(); services.TryAddSingleton<ICredentialProvider, DefaultCredentialProvider>(); services.TryAddSingleton<IClient, DefaultStorageClient>(); return services; }
总结
目前对象存储临时并未反对多租户、多环境,后续依据状况逐步完善减少多租户、多环境反对,以适配不同的租户、不同的环境下的对象存储到指定的 Bucket
中
本章源码
Assignment06
https://github.com/zhenlei520…
开源地址
MASA.BuildingBlocks:https://github.com/masastack/…
MASA.Contrib:https://github.com/masastack/…
MASA.Utils:https://github.com/masastack/…
MASA.EShop:https://github.com/masalabs/M…
MASA.Blazor:https://github.com/BlazorComp…
如果你对咱们的 MASA Framework 感兴趣,无论是代码奉献、应用、提 Issue,欢送分割咱们