Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Infrastructure/OSS/OssService.cs
glz 77778f67c1 feat: 新增SDK模块、OSS相关功能并优化Redis与代码扩展
1.  新增API版本枚举SDK项,创建SDK基础控制器
2.  添加IList、string扩展方法,新增时间戳扩展工具
3.  新增OSS配置、STS凭证服务、文件处理相关代码
4.  重构Redis缓存组件,替换StackExchange.Redis为CSRedisCore
5.  修复原有Redis操作方法调用,新增基础控制器快捷返回方法
6.  新增阿里云OSS、短信、VOD相关SDK依赖
7.  配置阿里云OSS相关参数到appsettings
2026-06-09 14:36:46 +08:00

308 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Aliyun.Acs.Core;
using Aliyun.Acs.Core.Auth.Sts;
using Aliyun.Acs.Core.Profile;
using Aliyun.OSS;
using Aliyun.OSS.Common;
using CSRedis;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using QYZH.InteractiveMagazine.Common.Extensions;
using System.Text.Json;
using System.Web;
namespace QYZH.InteractiveMagazine.Infrastructure.OSS
{
public class OssService
{
private readonly OssClient _ossClient;
private readonly OSSOptions _ossOption;
private readonly CSRedisClient _redis;
private readonly ILogger<OssService> _logger;
//private readonly IOssResourceRepository _ossResourceRepository;
public OssService(IOptionsMonitor<OSSOptions> options, CSRedisClient redis, ILogger<OssService> logger)
{
// _ossOption = configuration.GetSection("AliyunOSSConfigs").Get<OSSOptions>();
_ossOption = options.CurrentValue;
_ossClient = new OssClient($"oss-cn-{_ossOption.Region}.aliyuncs.com", _ossOption.AccessKeyID, _ossOption.AccessKeySecret);
_ossClient.SetRegion($"cn-{_ossOption.Region}");
_redis = redis;
_logger = logger;
//_ossResourceRepository = ossResourceRepository;
}
public async Task<OssCredentials> GetSTSTokenAsync(CancellationToken cancellationToken)
{
var stsJson = await _redis.GetAsync("STS-InteractiveMagazine-OssCredentials");
if (string.IsNullOrWhiteSpace(stsJson))
{
// ststoken
IClientProfile profile = DefaultProfile.GetProfile($"oss-cn-{_ossOption.Region}", _ossOption.AccessKeyID, _ossOption.AccessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
var policy = new
{
Version = "1",
Statement = new List<dynamic>
{
new
{
Action = new List<string> { "oss:PutObject", "oss:GetObject","oss:DeleteObject" },
Resource = new List<string> { $"acs:oss:*:*:{_ossOption.BucketName}/temp/*" },
//Condition = new
//{
// NumericLessThanEquals = new Dictionary<string, object>()
// {
// {"oss:ContentLength",104857600 }
// }
//},
Effect = "Allow",
}
}
};
var assumeRoleRequest = new AssumeRoleRequest();
assumeRoleRequest.Policy = JsonSerializer.Serialize(policy);
assumeRoleRequest.RoleArn = _ossOption.RoleArn;
assumeRoleRequest.RoleSessionName = "oss-services";//这里的名字随便写
assumeRoleRequest.DurationSeconds = 3600;//过期时间
var response = client.GetAcsResponse(assumeRoleRequest);
var expiration = DateTime.Parse(response.Credentials.Expiration).AddMinutes(5);
var result = new OssCredentials
{
AccessKeyId = response.Credentials.AccessKeyId,
AccessKeySecret = response.Credentials.AccessKeySecret,
SecurityToken = response.Credentials.SecurityToken,
ExpireTime = expiration.ToMillisecondsTimestamp(),
BucketName = _ossOption.BucketName,
Region = $"oss-cn-{_ossOption.Region}",
Domain = _ossOption.Domain
};
await _redis.SetAsync("STS-InteractiveMagazine-OssCredentials", result, expiration - DateTime.Now);
return result;
}
else
{
return JsonSerializer.Deserialize<OssCredentials>(stsJson);
}
}
public async Task<Dictionary<string, string>> GetSTSToken()
{
var stsJson = await _redis.GetAsync("QYZH.InteractiveMagazine.STS");
if (stsJson.IsNull())
{
// ststoken
IClientProfile profile = DefaultProfile.GetProfile($"oss-cn-{_ossOption.Region}", _ossOption.AccessKeyID, _ossOption.AccessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
var policy = new
{
Version = "1",
Statement = new List<dynamic>
{
new
{
Action = new List<string> { "oss:PutObject", "oss:GetObject","oss:DeleteObject" },
Resource = new List<string> { $"acs:oss:*:*:{_ossOption.BucketName}/temp/*" },
//Condition = new
//{
// NumericLessThanEquals = new Dictionary<string, object>()
// {
// {"oss:ContentLength",104857600 }
// }
//},
Effect = "Allow",
}
}
};
var request = new AssumeRoleRequest();
request.Policy = JsonSerializer.Serialize(policy);
request.RoleArn = _ossOption.RoleArn;
request.RoleSessionName = "oss-services";//这里的名字随便写
request.DurationSeconds = 3600;//过期时间
var response = client.GetAcsResponse(request);
var expiration = DateTime.Parse(response.Credentials.Expiration);
var result = new Dictionary<string, string>
{
{"AccessKeyId", response.Credentials.AccessKeyId},
{"AccessKeySecret",response.Credentials.AccessKeySecret },
{"SecurityToken",response.Credentials.SecurityToken },
{"Expiration", DateTime.Parse(response.Credentials.Expiration).ToString("yyyy-MM-dd HH:mm:ss") },
{"BucketName",_ossOption.BucketName },
{"Region",$"oss-cn-{_ossOption.Region }"},
{"Domain",_ossOption.Domain }
};
var seconds = (expiration - DateTime.Now).TotalSeconds - 300;
await _redis.SetAsync("QYZH.InteractiveMagazine.STS", result, TimeSpan.FromSeconds(seconds));
return result;
}
return JsonSerializer.Deserialize<Dictionary<string, string>>(stsJson);
}
/// <summary>
/// 上传单个
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string PutObject(string key, Stream content)
{
//try
//{
var result = _ossClient.PutObject(_ossOption.BucketName, key, content);
if (result.HttpStatusCode == System.Net.HttpStatusCode.OK)
{
return key;
}
else return null;
//}
//catch (Exception ex)
//{
// return null;
//}
}
/// <summary>
/// 删除单个
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public bool DeleteObject(string key)
{
try
{
if (key.IsNull()) return false;
key = HttpUtility.UrlDecode(key);
//_ossResourceRepository.UpdateDiy.Set(s => s.Status, -1).Where(w => w.Path == key).ExecuteAffrows();
var result = _ossClient.DeleteObject(_ossOption.BucketName, key);
return result.HttpStatusCode == System.Net.HttpStatusCode.OK;
}
catch (OssException ex)
{
_logger.LogError(ex.Message + ex.StackTrace);
return false;
}
}
/// <summary>
/// 删除多个
/// </summary>
/// <param name="keys"></param>
/// <returns></returns>
public bool DeleteObjects(List<string>? keys)
{
try
{
if (keys.IsNull()) return false;
// 设置为详细模式,返回所有删除的文件列表。
var quietMode = false;
keys = keys.Select(it => it.RemoveDomain()).ToList();
//_ossResourceRepository.UpdateDiy.Set(s => s.Status, -1).Where(w => keys.Contains(w.Path)).ExecuteAffrows();
var request = new DeleteObjectsRequest(_ossOption.BucketName, keys, quietMode);
// 删除多个文件。
var result = _ossClient.DeleteObjects(request);
//var del = _ossResourceRepository.Delete(w => keys.Contains(w.Path));
//AppException.ThrowIf(!del, "删除资源失败");
return result.Keys.Count() == keys.Count;
}
catch (OssException ex)
{
_logger.LogError(ex.Message + ex.StackTrace);
return false;
}
}
public List<string> ListObjects(string prefix)
{
try
{
var keys = new List<string>();
ObjectListing result = null;
string nextMarker = string.Empty;
do
{
var listObjectsRequest = new ListObjectsRequest(_ossOption.BucketName)
{
Marker = nextMarker,
MaxKeys = 100,
Prefix = prefix,
};
result = _ossClient.ListObjects(listObjectsRequest);
foreach (var summary in result.ObjectSummaries)
{
keys.Add(summary.Key);
}
nextMarker = result.NextMarker;
} while (result.IsTruncated);
return keys;
}
catch (OssException ex)
{
_logger.LogError(ex.Message + ex.StackTrace);
return null;
}
}
/// <summary>
/// 复制文件
/// </summary>
/// <param name="sourceObject"></param>
/// <param name="targetObject"></param>
/// <returns></returns>
public bool CopyObject(string sourceObject, string targetObject, long? id = null)
{
try
{
//var metadata = new ObjectMetadata();
// 设置自定义元数据。自定义元数据为键值对Key-Value的形式例如Key为mk1Value为mv1。
//metadata.AddHeader("mk1", "mv1");
//metadata.AddHeader("mk2", "mv2");
sourceObject = sourceObject.RemoveDomain();
sourceObject = Uri.UnescapeDataString(sourceObject);
//var uow = _unitOfWorkManager.Begin();
//var data = _ossResourceRepository.Insert(new OssResource
//{
// MainId = id,
// SoucePath = sourceObject,
// Path = targetObject,
//});
//AppException.ThrowIf(data.IsNull(), "资源添加失败");
var req = new CopyObjectRequest(_ossOption.BucketName, sourceObject, _ossOption.BucketName, targetObject)
{
// 如果NewObjectMetadata为null则为COPY模式即拷贝源文件的元数据非null则为REPLACE模式覆盖源文件的元数据
NewObjectMetadata = null
};
var result = _ossClient.CopyObject(req);
return result.HttpStatusCode == System.Net.HttpStatusCode.OK;
}
catch (OssException ex)
{
_logger.LogError(ex.Message + ex.StackTrace);
return false;
}
}
}
}