feat(storage): refactor storage service and provider interface, support dynamic registration of storage providers

This commit is contained in:
ShiYu
2025-05-22 16:31:32 +08:00
parent cafe48402e
commit 9243a26189
15 changed files with 237 additions and 105 deletions

View File

@@ -2,10 +2,10 @@ using Foxel.Services.Interface;
using COSXML;
using COSXML.Auth;
using COSXML.Model.Object;
using COSXML.Model.Bucket;
using COSXML.Transfer;
using COSXML.CosException;
using COSXML.Model.Tag;
using Foxel.Services.Attributes;
namespace Foxel.Services.StorageProvider;
@@ -20,7 +20,7 @@ public class CustomQCloudCredentialProvider : DefaultSessionQCloudCredentialProv
Refresh();
}
public override void Refresh()
public sealed override void Refresh()
{
try
{
@@ -29,8 +29,8 @@ public class CustomQCloudCredentialProvider : DefaultSessionQCloudCredentialProv
string tmpToken = _configService["Storage:CosStorageToken"];
long tmpStartTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
long tmpExpiredTime = tmpStartTime + 7200;
SetQCloudCredential(tmpSecretId, tmpSecretKey,
String.Format("{0};{1}", tmpStartTime, tmpExpiredTime), tmpToken);
SetQCloudCredential(tmpSecretId, tmpSecretKey,
$"{tmpStartTime};{tmpExpiredTime}", tmpToken);
}
catch (Exception ex)
{
@@ -39,11 +39,9 @@ public class CustomQCloudCredentialProvider : DefaultSessionQCloudCredentialProv
}
}
}
[StorageProvider(StorageType.Cos)]
public class CosStorageProvider : IStorageProvider
{
private readonly string _secretId;
private readonly string _secretKey;
private readonly string _bucketName;
private readonly string _region;
private readonly string _cdnUrl;
@@ -55,14 +53,12 @@ public class CosStorageProvider : IStorageProvider
public CosStorageProvider(IConfigService configService)
{
_configService = configService;
_secretId = configService["Storage:CosStorageSecretId"];
_secretKey = configService["Storage:CosStorageSecretKey"];
_bucketName = configService["Storage:CosStorageBucketName"];
_region = configService["Storage:CosStorageRegion"];
_cdnUrl = configService["Storage:CosStorageCdnUrl"] ?? string.Empty;
_cdnUrl = configService["Storage:CosStorageCdnUrl"];
// 检查桶是否为公开读取(从配置获取)
bool.TryParse(configService["Storage:CosStoragePublicRead"] ?? "false", out _isPublicRead);
bool.TryParse(configService["Storage:CosStoragePublicRead"], out _isPublicRead);
// 在构造函数中初始化客户端,作为单例使用
_cosXmlClient = CreateClient();
@@ -96,7 +92,7 @@ public class CosStorageProvider : IStorageProvider
string tempPath = Path.GetTempFileName();
try
{
using (var fileStream2 = new FileStream(tempPath, FileMode.Create))
await using (var fileStream2 = new FileStream(tempPath, FileMode.Create))
{
await fileStream.CopyToAsync(fileStream2);
}
@@ -105,7 +101,7 @@ public class CosStorageProvider : IStorageProvider
var transferManager = new TransferManager(_cosXmlClient, transferConfig);
var uploadTask = new COSXMLUploadTask(_bucketName, objectKey);
uploadTask.SetSrcPath(tempPath);
var result = await transferManager.UploadAsync(uploadTask);
await transferManager.UploadAsync(uploadTask);
return objectKey;
}
finally
@@ -216,7 +212,7 @@ public class CosStorageProvider : IStorageProvider
var transferConfig = new TransferConfig();
var transferManager = new TransferManager(_cosXmlClient, transferConfig);
var downloadTask = new COSXMLDownloadTask(_bucketName, storagePath, tempDir, fileName);
var result = await transferManager.DownloadAsync(downloadTask);
await transferManager.DownloadAsync(downloadTask);
return localFilePath;
}
catch (CosClientException clientEx)

View File

@@ -1,7 +1,9 @@
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
namespace Foxel.Services.StorageProvider;
[StorageProvider(StorageType.Local)]
public class LocalStorageProvider(IConfigService config) : IStorageProvider
{
private readonly string _baseDirectory = Path.Combine(Directory.GetCurrentDirectory(), "Uploads");

View File

@@ -1,3 +1,4 @@
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
using Amazon.S3;
using Amazon.S3.Model;
@@ -5,6 +6,7 @@ using Amazon.S3.Transfer;
namespace Foxel.Services.StorageProvider;
[StorageProvider(StorageType.S3)]
public class S3StorageProvider : IStorageProvider
{
private readonly string _accessKey;
@@ -13,7 +15,6 @@ public class S3StorageProvider : IStorageProvider
private readonly string _region;
private readonly string _endpoint;
private readonly bool _usePathStyleUrls;
private readonly string _serverUrl;
private readonly string _cdnUrl;
public S3StorageProvider(IConfigService configService)
@@ -22,9 +23,8 @@ public class S3StorageProvider : IStorageProvider
_secretKey = configService["Storage:S3StorageSecretKey"];
_bucketName = configService["Storage:S3StorageBucketName"];
_region = configService["Storage:S3StorageRegion"];
_serverUrl = configService["AppSettings:ServerUrl"];
_cdnUrl = configService["Storage:S3StorageCdnUrl"] ?? string.Empty;
_endpoint = configService["Storage:S3StorageEndpoint"] ?? $"https://s3.{_region}.amazonaws.com";
_cdnUrl = configService["Storage:S3StorageCdnUrl"];
_endpoint = configService["Storage:S3StorageEndpoint"];
_usePathStyleUrls = bool.TryParse(configService["Storage:S3StorageUsePathStyleUrls"], out var usePathStyle) && usePathStyle;
}
@@ -163,7 +163,7 @@ public class S3StorageProvider : IStorageProvider
};
using var response = await client.GetObjectAsync(request);
using var fileStream = new FileStream(tempFilePath, FileMode.Create);
await using var fileStream = new FileStream(tempFilePath, FileMode.Create);
await response.ResponseStream.CopyToAsync(fileStream);
return tempFilePath;

View File

@@ -1,3 +1,4 @@
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
using System.Net.Http.Headers;
using System.Text.Json;
@@ -5,6 +6,7 @@ using System.Text.Json.Serialization;
namespace Foxel.Services.StorageProvider;
[StorageProvider(StorageType.Telegram)]
public class TelegramStorageProvider(IConfigService configService) : IStorageProvider
{
private readonly string _botToken = configService["Storage:TelegramStorageBotToken"];
@@ -83,7 +85,7 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
using var httpClient = new HttpClient();
var url =
$"https://api.telegram.org/bot{_botToken}/deleteMessage?chat_id={metadata.ChatId}&message_id={metadata.MessageId}";
var response = await httpClient.GetAsync(url);
await httpClient.GetAsync(url);
}
catch (Exception ex)
{
@@ -165,8 +167,8 @@ public class TelegramStorageProvider(IConfigService configService) : IStoragePro
string tempFilePath = Path.Combine(tempDir, tempFileName);
// 保存文件
using var fileStream = await fileResponse.Content.ReadAsStreamAsync();
using var outputStream = new FileStream(tempFilePath, FileMode.Create);
await using var fileStream = await fileResponse.Content.ReadAsStreamAsync();
await using var outputStream = new FileStream(tempFilePath, FileMode.Create);
await fileStream.CopyToAsync(outputStream);
return tempFilePath;