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
This commit is contained in:
307
QYZH.InteractiveMagazine.Infrastructure/OSS/OssService.cs
Normal file
307
QYZH.InteractiveMagazine.Infrastructure/OSS/OssService.cs
Normal file
@ -0,0 +1,307 @@
|
||||
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为mk1,Value为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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user