live-forum/server/webapi/LiveForum/LiveForum.Service/Others/CosService.cs
2026-03-24 11:27:37 +08:00

214 lines
8.3 KiB
C#
Raw Permalink 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 COSXML;
using COSXML.Auth;
using COSXML.Model.Object;
using COSSTS;
using LiveForum.Code.Base;
using LiveForum.IService.Others;
using LiveForum.Model.Dto.Others;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using COSXML.Model.Tag;
namespace LiveForum.Service.Others
{
/// <summary>
/// 腾讯云COS服务实现
/// </summary>
public class CosService : ICosService
{
private readonly TencentCosConfig _config;
private readonly CosXml _cosXml;
private readonly CosXmlConfig _cosXmlConfig;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="config">COS配置</param>
public CosService(IOptions<TencentCosConfig> config)
{
_config = config.Value;
// 初始化COS配置
_cosXmlConfig = new CosXmlConfig.Builder()
.SetRegion(_config.Region)
.Build();
// 创建凭证提供者
var qCloudCredentialProvider = new DefaultQCloudCredentialProvider(
_config.SecretId,
_config.SecretKey,
_config.DurationSecond);
// 初始化COS客户端
_cosXml = new CosXmlServer(_cosXmlConfig, qCloudCredentialProvider);
}
/// <summary>
/// 生成签名URL
/// </summary>
/// <param name="request">请求参数</param>
/// <returns>签名URL和过期时间</returns>
public async Task<BaseResponse<CosGenerateSignRespDto>> GenerateSignURL(CosGenerateSignReq request)
{
try
{
var preSignatureStruct = new PreSignatureStruct
{
appid = _config.AppId,
region = _config.Region,
bucket = $"{_config.BucketName}-{_config.AppId}",
key = request.Key,
httpMethod = request.HttpMethod,
isHttps = request.IsHttps,
signDurationSecond = _config.DurationSecond,
headers = null,
queryParameters = null
};
// 生成预签名URL
string signUrl = _cosXml.GenerateSignURL(preSignatureStruct);
var response = new CosGenerateSignRespDto
{
SignUrl = signUrl,
ExpiredSeconds = (int)_config.DurationSecond
};
return new BaseResponse<CosGenerateSignRespDto>(response);
}
catch (COSXML.CosException.CosClientException clientEx)
{
return new BaseResponse<CosGenerateSignRespDto>(
ResponseCode.Error,
$"生成签名URL失败客户端错误{clientEx.Message}");
}
catch (COSXML.CosException.CosServerException serverEx)
{
return new BaseResponse<CosGenerateSignRespDto>(
ResponseCode.Error,
$"生成签名URL失败服务器错误{serverEx.GetInfo()}");
}
catch (Exception ex)
{
return new BaseResponse<CosGenerateSignRespDto>(
ResponseCode.Error,
$"生成签名URL失败{ex.Message}");
}
}
/// <summary>
/// 生成临时密钥
/// </summary>
/// <param name="request">请求参数</param>
/// <returns>临时密钥信息</returns>
public async Task<BaseResponse<CosGenerateTemporaryKeyRespDto>> GenerateTemporaryKey(CosGenerateTemporaryKeyReq request)
{
try
{
// 验证和设置参数
string bucket = $"{_config.BucketName}-{_config.AppId}";
string region = _config.Region;
string prefixes = request.Prefixes ?? _config.Prefixes;
// 允许的路径前缀
string allowPrefix = "*";
// 密钥的权限列表
string[] allowActions = new string[]
{
"name/cos:PutObject",
"name/cos:PostObject",
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload"
};
// 设置参数
Dictionary<string, object> values = new Dictionary<string, object>();
values.Add("bucket", bucket);
values.Add("region", region);
values.Add("allowPrefix", allowPrefix);
values.Add("allowPrefixes", new string[] { string.IsNullOrEmpty(prefixes) ? "file/*" : $"{prefixes}/*" });
values.Add("allowActions", allowActions);
values.Add("durationSeconds", _config.DurationSecond);
values.Add("secretId", _config.SecretId);
values.Add("secretKey", _config.SecretKey);
// 生成临时凭证
Dictionary<string, object> credential = STSClient.genCredential(values);
// 手动映射字段因为STS返回的字段名可能与DTO不完全匹配
var tempKeyEntity = new CosGenerateTemporaryKeyRespDto
{
Bucket = bucket,
Region = region,
Prefixes = prefixes
};
// 提取凭证信息
if (credential.TryGetValue("credentials", out var credsObj) && credsObj is Dictionary<string, object> credsDict)
{
tempKeyEntity.Credentials = new CredentialsDto
{
Token = credsDict.TryGetValue("token", out var token) ? token?.ToString() ?? string.Empty : string.Empty,
TmpSecretId = credsDict.TryGetValue("tmpSecretId", out var tmpSecretId) ? tmpSecretId?.ToString() ?? string.Empty : string.Empty,
TmpSecretKey = credsDict.TryGetValue("tmpSecretKey", out var tmpSecretKey) ? tmpSecretKey?.ToString() ?? string.Empty : string.Empty
};
}
// 提取时间信息
if (credential.TryGetValue("expiration", out var expiration))
{
tempKeyEntity.Expiration = expiration?.ToString() ?? string.Empty;
}
if (credential.TryGetValue("expiredTime", out var expiredTime) && expiredTime != null)
{
if (long.TryParse(expiredTime.ToString(), out long expiredTimeLong))
{
tempKeyEntity.ExpiredTime = expiredTimeLong;
}
}
if (credential.TryGetValue("startTime", out var startTime) && startTime != null)
{
if (long.TryParse(startTime.ToString(), out long startTimeLong))
{
tempKeyEntity.StartTime = startTimeLong;
}
}
// 生成文件路径
string modelName = string.IsNullOrEmpty(request.ModelName) ? "images" : request.ModelName;
string fileName = request.FileName;
if (!string.IsNullOrEmpty(fileName))
{
var ext = System.IO.Path.GetExtension(fileName);
if (!string.IsNullOrEmpty(ext))
{
// 使用 UTC 时间生成时间戳文件名
fileName = $"{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}{ext}";
}
}
tempKeyEntity.FilePath = $"{prefixes}/{modelName}/{DateTime.Now:yyyyMMdd}/{fileName}";
tempKeyEntity.DomainName = _config.DomainUrl;
return new BaseResponse<CosGenerateTemporaryKeyRespDto>(tempKeyEntity);
}
catch (Exception ex)
{
return new BaseResponse<CosGenerateTemporaryKeyRespDto>(
ResponseCode.Error,
$"生成临时密钥失败:{ex.Message}");
}
}
}
}