|
|
using OnceMi.AspNetCore.OSS;
|
|
|
|
|
|
namespace Admin.NET.Core.Service;
|
|
|
|
|
|
/// <summary>
|
|
|
/// OSS服务管理器接口
|
|
|
/// </summary>
|
|
|
public interface IOSSServiceManager : IDisposable
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 获取OSS服务实例
|
|
|
/// </summary>
|
|
|
/// <param name="provider">存储提供者配置</param>
|
|
|
/// <returns></returns>
|
|
|
Task<IOSSService> GetOSSServiceAsync(SysFileProvider provider);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 清除缓存
|
|
|
/// </summary>
|
|
|
void ClearCache();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// OSS服务管理器实现
|
|
|
/// </summary>
|
|
|
public class OSSServiceManager : IOSSServiceManager, ITransient
|
|
|
{
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
private readonly ConcurrentDictionary<string, IOSSService> _ossServiceCache;
|
|
|
private readonly object _lockObject = new object();
|
|
|
private bool _disposed = false;
|
|
|
|
|
|
public OSSServiceManager(IServiceProvider serviceProvider)
|
|
|
{
|
|
|
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
|
|
|
_ossServiceCache = new ConcurrentDictionary<string, IOSSService>();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取OSS服务实例(带缓存)
|
|
|
/// </summary>
|
|
|
/// <param name="provider">存储提供者配置</param>
|
|
|
/// <returns></returns>
|
|
|
public async Task<IOSSService> GetOSSServiceAsync(SysFileProvider provider)
|
|
|
{
|
|
|
if (provider == null)
|
|
|
throw new ArgumentNullException(nameof(provider));
|
|
|
|
|
|
var cacheKey = provider.ConfigKey;
|
|
|
|
|
|
// 尝试从缓存获取
|
|
|
if (_ossServiceCache.TryGetValue(cacheKey, out var cachedService))
|
|
|
{
|
|
|
return cachedService;
|
|
|
}
|
|
|
|
|
|
// 验证配置
|
|
|
if (!await ValidateConfigurationAsync(provider))
|
|
|
{
|
|
|
throw new InvalidOperationException($"OSS提供者配置无效: {provider.DisplayName}");
|
|
|
}
|
|
|
|
|
|
// 线程安全地创建新服务
|
|
|
lock (_lockObject)
|
|
|
{
|
|
|
// 双重检查锁定模式
|
|
|
if (_ossServiceCache.TryGetValue(cacheKey, out cachedService))
|
|
|
{
|
|
|
return cachedService;
|
|
|
}
|
|
|
|
|
|
// 转换配置并创建服务
|
|
|
var ossOptions = ConvertToOSSOptions(provider);
|
|
|
var ossService = CreateOSSService(ossOptions);
|
|
|
|
|
|
// 添加到缓存
|
|
|
_ossServiceCache.TryAdd(cacheKey, ossService);
|
|
|
|
|
|
return ossService;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 创建OSS服务实例
|
|
|
/// </summary>
|
|
|
/// <param name="options">OSS配置选项</param>
|
|
|
/// <returns></returns>
|
|
|
private IOSSService CreateOSSService(OSSOptions options)
|
|
|
{
|
|
|
ArgumentNullException.ThrowIfNull(options);
|
|
|
|
|
|
try
|
|
|
{
|
|
|
// 使用现有的IOSSServiceFactory,但需要先注册配置
|
|
|
var providerName = Enum.GetName(options.Provider);
|
|
|
var configSectionName = $"TempOSS_{Guid.NewGuid():N}";
|
|
|
|
|
|
// 创建临时配置
|
|
|
var configData = new Dictionary<string, string>
|
|
|
{
|
|
|
[$"{configSectionName}:Provider"] = providerName,
|
|
|
[$"{configSectionName}:Endpoint"] = options.Endpoint ?? "",
|
|
|
[$"{configSectionName}:AccessKey"] = options.AccessKey ?? "",
|
|
|
[$"{configSectionName}:SecretKey"] = options.SecretKey ?? "",
|
|
|
[$"{configSectionName}:Region"] = options.Region ?? "",
|
|
|
[$"{configSectionName}:IsEnableHttps"] = options.IsEnableHttps.ToString(),
|
|
|
[$"{configSectionName}:IsEnableCache"] = options.IsEnableCache.ToString()
|
|
|
};
|
|
|
|
|
|
var tempConfig = new ConfigurationBuilder()
|
|
|
.AddInMemoryCollection(configData)
|
|
|
.Build();
|
|
|
|
|
|
// 创建临时服务集合,但不立即释放
|
|
|
var services = new ServiceCollection();
|
|
|
services.AddSingleton<IConfiguration>(tempConfig);
|
|
|
services.AddLogging();
|
|
|
services.AddOSSService(providerName, configSectionName);
|
|
|
|
|
|
// 构建服务提供者并创建OSS服务
|
|
|
var tempServiceProvider = services.BuildServiceProvider();
|
|
|
var ossServiceFactory = tempServiceProvider.GetRequiredService<IOSSServiceFactory>();
|
|
|
var ossService = ossServiceFactory.Create(providerName);
|
|
|
|
|
|
// 注意:不要释放tempServiceProvider,因为ossService可能依赖它
|
|
|
// 这里我们接受这个内存开销,因为缓存会减少创建频率
|
|
|
|
|
|
return ossService;
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
throw Oops.Oh($"创建OSS服务失败: {ex.Message}");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 验证配置
|
|
|
/// </summary>
|
|
|
/// <param name="provider">存储提供者配置</param>
|
|
|
/// <returns></returns>
|
|
|
private Task<bool> ValidateConfigurationAsync(SysFileProvider provider)
|
|
|
{
|
|
|
if (provider == null) return Task.FromResult(false);
|
|
|
|
|
|
// 基本字段验证
|
|
|
var isValid = !string.IsNullOrWhiteSpace(provider.Provider) &&
|
|
|
!string.IsNullOrWhiteSpace(provider.BucketName) &&
|
|
|
!string.IsNullOrWhiteSpace(provider.AccessKey) &&
|
|
|
!string.IsNullOrWhiteSpace(provider.SecretKey);
|
|
|
|
|
|
// Minio额外需要Endpoint
|
|
|
if (provider.Provider.ToUpper() == "MINIO")
|
|
|
{
|
|
|
isValid = isValid && !string.IsNullOrWhiteSpace(provider.Endpoint);
|
|
|
}
|
|
|
|
|
|
return Task.FromResult(isValid);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 将SysFileProvider转换为OSSOptions
|
|
|
/// </summary>
|
|
|
/// <param name="provider"></param>
|
|
|
/// <returns></returns>
|
|
|
private OSSOptions ConvertToOSSOptions(SysFileProvider provider)
|
|
|
{
|
|
|
if (provider == null)
|
|
|
throw new ArgumentNullException(nameof(provider));
|
|
|
|
|
|
var ossOptions = new OSSOptions
|
|
|
{
|
|
|
Provider = Enum.Parse<OSSProvider>(provider.Provider),
|
|
|
Endpoint = provider.Endpoint,
|
|
|
Region = provider.Region,
|
|
|
IsEnableHttps = provider.IsEnableHttps ?? true,
|
|
|
IsEnableCache = provider.IsEnableCache ?? true
|
|
|
};
|
|
|
|
|
|
// 设置认证信息(所有提供者现在都使用统一的字段)
|
|
|
ossOptions.AccessKey = provider.AccessKey;
|
|
|
ossOptions.SecretKey = provider.SecretKey;
|
|
|
|
|
|
return ossOptions;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 清除缓存
|
|
|
/// </summary>
|
|
|
public void ClearCache()
|
|
|
{
|
|
|
lock (_lockObject)
|
|
|
{
|
|
|
_ossServiceCache.Clear();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 释放资源
|
|
|
/// </summary>
|
|
|
public void Dispose()
|
|
|
{
|
|
|
if (!_disposed)
|
|
|
{
|
|
|
lock (_lockObject)
|
|
|
{
|
|
|
_ossServiceCache.Clear();
|
|
|
}
|
|
|
_disposed = true;
|
|
|
}
|
|
|
}
|
|
|
} |