live-forum/server/webapi/需求文件/cos.md
2026-03-24 11:27:37 +08:00

14 KiB
Raw Blame History

腾讯云cos

1.由于服务的限制现需要在项目中集成腾讯cos服务。需要实现的功能上传文件。返回签名。生成临时密钥。

上传文件需要实现的功能。

1.需要实现接口IFileUploadService需要实现的原因时后面可以通过依赖注入去直接在系统全局中把原来的上传的本地变成上传到cos上。 后端\项目\LiveForum\LiveForum.WebApi\Controllers\FileUploadController.cs 这个是上传到本地的控制器等腾讯云实现了会修改依赖注入的IFileUploadService对象。

返回签名、生成临时密钥 这个需要新增cos服务接口新增cos控制器新增对应的接口。

1.由于服务器的带宽太小所以有备选方案前端在请求签名后去生成临时密钥直接去上传到cos不经过服务器了。 参考下面代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MiaoYu.Core.Cos.Models
{
    /// <summary>
    /// 获取临时访问凭证
    /// </summary>
    public class CodeCosGenerateTemporaryKeyEntity
    {
        /// <summary>
        /// 临时访问凭证
        /// </summary>
        public Credentials Credentials { get; set; }
        /// <summary>
        /// 临时访问凭证有效的时间,返回 Unix 时间戳,精确到秒
        /// </summary>
        public string Expiration { get; set; }
        /// <summary>
        /// 临时访问凭证有效的时间,以 iso8601 格式的 UTC 时间表示 注意:此字段可能返回 null表示取不到有效值。
        /// </summary>
        public long ExpiredTime { get; set; }

        /// <summary>
        /// 开始时间
        /// </summary>
        public long StartTime { get; set; }
        /// <summary>
        /// 唯一请求 ID由服务端生成每次请求都会返回若请求因其他原因未能抵达服务端则该次请求不会获得 RequestId。定位问题时需要提供该次请求的 RequestId。
        /// </summary>
        public string RequestId { get; set; }
    }

    /// <summary>
    /// 临时访问凭证
    /// </summary>
    public class Credentials
    {
        /// <summary>
        /// Token
        /// </summary>
        public string Token { get; set; }
        /// <summary>
        /// SecretId
        /// </summary>
        public string TmpSecretId { get; set; }
        /// <summary>
        /// SecretKey
        /// </summary>
        public string TmpSecretKey { get; set; }
    }

}

/// <summary>
/// 
/// </summary>
public interface ICodeCosService : ISingletonDependency //:ITransientDependency: IScopedDependency
{
    /// <summary>
    /// 返回签名地址  
    /// </summary>
    /// <param name="cosGenerateSign"></param>
    /// <returns>sign签名值,过期时间</returns>
    (string sign, int expiredSeconds) GenerateSignURL(CosGenerateSign cosGenerateSign);
    /// <summary>
    /// 生成临时密钥
    /// </summary>
    /// <returns></returns>
    CodeCosGenerateTemporaryKeyEntity GenerateTemporaryKey(CosGenerateSign cosGenerateSign);

}

using COSXML.Model.Object;
using COSXML.Model.Tag;
using COSXML;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using COSXML.Auth;
using MiaoYu.Core.Cos.Configs;
using NPOI.SS.Formula.Functions;
using COSSTS;
using Newtonsoft.Json;
using System.Collections;
using TencentCloud.Tci.V20190318.Models;

namespace MiaoYu.Core.Cos.Services.Impl
{
    /// <summary>
    /// 腾讯云 
    /// </summary>
    public class TencentCodeCosService : ICodeCosService//ISingletonDependency //: IScopedDependency//: ITransientDependency
    {
        //public TencentCodeCosService() { }
        private CosXml cosXml;
        private TencentCosConfig tencentCosConfig;
        private TencentConfig tencentConfig;
        CosXmlConfig config = null;
        public TencentCodeCosService(TencentConfig tencentConfig)
        {
            this.tencentConfig = tencentConfig;
            this.tencentCosConfig = tencentConfig.CosConfig;
            if (config == null)
            {
                config = new CosXmlConfig.Builder()
                     //.SetRegion("COS_REGION") // 设置默认的区域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
                     .Build();
            }
            string secretId = tencentCosConfig.SecretId;   // 云 API 密钥 SecretId, 获取 API 密钥请参照 https://console.cloud.tencent.com/cam/capi
            string secretKey = tencentCosConfig.SecretKey; // 云 API 密钥 SecretKey, 获取 API 密钥请参照 https://console.cloud.tencent.com/cam/capi
            long durationSecond = tencentCosConfig.DurationSecond;          //每次请求签名有效时长,单位为秒
            QCloudCredentialProvider qCloudCredentialProvider = new DefaultQCloudCredentialProvider(secretId,
              secretKey, durationSecond);

            this.cosXml = new CosXmlServer(config, qCloudCredentialProvider);
        }
        public (string sign, int expiredSeconds) GenerateSignURL(CosGenerateSign cosGenerateSign)
        {
            try
            {
                PreSignatureStruct preSignatureStruct = new PreSignatureStruct();
                // APPID 获取参考 https://console.cloud.tencent.com/developer
                preSignatureStruct.appid = tencentCosConfig.AppId;

                // 存储桶所在地域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
                preSignatureStruct.region = tencentCosConfig.Region;
                // 存储桶名称,此处填入格式必须为 bucketname-APPID, 其中 APPID 获取参考 https://console.cloud.tencent.com/developer
                preSignatureStruct.bucket = tencentCosConfig.Bucket + "-" + tencentCosConfig.AppId;// "examplebucket-1250000000";
                preSignatureStruct.key = "exampleobject"; //对象键
                preSignatureStruct.httpMethod = "PUT"; //HTTP 请求方法
                preSignatureStruct.isHttps = true; //生成 HTTPS 请求 URL
                preSignatureStruct.signDurationSecond = tencentCosConfig.DurationSecond; //请求签名时间为 600s
                preSignatureStruct.headers = null;//签名中需要校验的 header
                preSignatureStruct.queryParameters = null; //签名中需要校验的 URL 中请求参数

                //上传预签名 URL (使用永久密钥方式计算的签名 URL)
                string requestSignURL = cosXml.GenerateSignURL(preSignatureStruct);

                return new(requestSignURL, (int)tencentCosConfig.DurationSecond);
            }
            catch (COSXML.CosException.CosClientException clientEx)
            {
                //请求失败
                Console.WriteLine("CosClientException: " + clientEx);
            }
            catch (COSXML.CosException.CosServerException serverEx)
            {
                //请求失败
                Console.WriteLine("CosServerException: " + serverEx.GetInfo());
            }
            throw new NotImplementedException();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cosGenerateSign"></param>
        /// <returns></returns>
        public CodeCosGenerateTemporaryKeyEntity GenerateTemporaryKey(CosGenerateSign cosGenerateSign)
        {
            #region 验证区域
            if (string.IsNullOrEmpty(cosGenerateSign.Bucket))
            {
                cosGenerateSign.Bucket = tencentCosConfig.Bucket;
            }
            if (string.IsNullOrEmpty(cosGenerateSign.AppId))
            {
                cosGenerateSign.AppId = tencentCosConfig.AppId;
            }
            if (string.IsNullOrEmpty(cosGenerateSign.Region))
            {
                cosGenerateSign.Region = tencentCosConfig.Region;
            }
            #endregion

            string bucket = cosGenerateSign.Bucket + "-" + cosGenerateSign.AppId; // 您的 bucket

            string region = cosGenerateSign.Region;// bucket 所在区域

            // 改成允许的路径前缀,根据自己网站的用户判断允许上传的路径,例子:a.jpg 或者 a/* 或者 * (通配符*存在重大安全风险, 谨慎评估使用)
            string allowPrefix = "*";
            /*
             * 密钥的权限列表。必须在这里指定本次临时密钥所需要的权限。权限列表请参见 https://cloud.tencent.com/document/product/436/31923
             * 规则为 {project}:{interfaceName}
             * project : 产品缩写  cos相关授权为值为cos,数据万象(数据处理)相关授权值为ci
             * 授权所有接口用*表示,例如 cos:*,ci:*
             */
            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);
            // 也可以通过 allowPrefixes 指定路径前缀的集合
            values.Add("allowPrefixes", new string[] {
                string.IsNullOrEmpty(cosGenerateSign.Prefixes)?"miaoyu/*":cosGenerateSign.Prefixes,
             });
            values.Add("allowActions", allowActions);
            values.Add("durationSeconds", 600);//指定临时证书的有效期, 参考 https://cloud.tencent.com/document/product/1312/48195
            values.Add("secretId", tencentCosConfig.SecretId);
            values.Add("secretKey", tencentCosConfig.SecretKey);

            Dictionary<string, object> credential = STSClient.genCredential(values); //返回值说明见README.md
            var json = JsonConvert.SerializeObject(credential);
            var person = JsonConvert.DeserializeObject<CodeCosGenerateTemporaryKeyEntity>(json);
            return person;
        }
    }
}

using MiaoYu.Api.Admin.ApplicationServices.Systems.Cos.Dtos;
using MiaoYu.Core.ApplicationServices;
using MiaoYu.Core.Cos.Configs;
using MiaoYu.Core.Cos.Models;
using MiaoYu.Core.Cos.Services;

namespace MiaoYu.Api.Admin.ApplicationServices.Systems.Cos;

/// <summary>
/// 腾讯云COS服务
/// </summary>
public class CosService : ApplicationService
{
    private readonly ICodeCosService _codeCosService;
    private readonly TencentConfig _tencentConfig;

    public CosService(ICodeCosService codeCosService, TencentConfig tencentConfig)
    {
        _codeCosService = codeCosService;
        _tencentConfig = tencentConfig;
    }

    /// <summary>
    /// 获取签名
    /// </summary>
    /// <returns></returns>
    public string GetCosSign()
    {
        var (sign, ex) = _codeCosService.GenerateSignURL(new CosGenerateSign());
        return sign;
    }

    /// <summary>
    /// 获取临时签名
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <param name="modelName">模型名称</param>
    /// <returns></returns>
    public GenerateTemporaryModel GetGenerateTemporaryKey(string fileName = "", string modelName = "")
    {
        var cosConfig = _tencentConfig.CosConfig;
        var t = new CosGenerateSign()
        {
            Prefixes = cosConfig.Prefixes ?? "file",
            Bucket = cosConfig?.Bucket,
            Region = cosConfig?.Region,
            AppId = cosConfig?.AppId,
            SecretId = cosConfig?.SecretId,
            SecretKey = cosConfig?.SecretKey,
            DurationSecond = cosConfig?.DurationSecond ?? 300
        };

        if (string.IsNullOrEmpty(modelName))
        {
            modelName = "images";
        }

        var tempFile = fileName;
        if (!string.IsNullOrEmpty(tempFile))
        {
            var ext = Path.GetExtension(tempFile);
            if (!string.IsNullOrEmpty(ext))
            {
                // 使用 UTC 时间生成时间戳文件名
                tempFile = $"{DateTimeOffset.UtcNow.ToUnixTimeSeconds()}{ext}";
            }
        }

        var model = _codeCosService.GenerateTemporaryKey(t);

        // 复制对象属性本项目中集成了Mapster框架使用Adapt转换即可
        var generateTemporaryModel = model.CopyObject<CodeCosGenerateTemporaryKeyEntity, GenerateTemporaryModel>() ?? new GenerateTemporaryModel();
        generateTemporaryModel.Bucket = t.Bucket + "-" + t.AppId;
        generateTemporaryModel.Region = t.Region;
        generateTemporaryModel.Prefixes = t.Prefixes;
        // 修复日期格式yyyMMdd -> yyyyMMdd
        generateTemporaryModel.FilePath = $"{t.Prefixes}/{modelName}/{DateTime.Now.ToString("yyyyMMdd")}/{tempFile}";
        generateTemporaryModel.DomainName = cosConfig.DomainName;

        return generateTemporaryModel;
    }
}

using MiaoYu.Api.Admin.ApplicationServices.Systems.Cos;
using MiaoYu.Api.Admin.ApplicationServices.Systems.Cos.Dtos;

namespace MiaoYu.Api.Admin.Controllers.Systems;

/// <summary>
/// 腾讯云COS控制器
/// </summary>
[ControllerDescriptor(DisplayName = "腾讯云COS")]
public class CosController : AdminControllerBase<CosService>
{
    public CosController(CosService defaultService)
        : base(defaultService)
    {
    }

    /// <summary>
    /// 获取COS签名
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ActionDescriptor(DisplayName = "获取COS签名")]
    public string GetCosSign()
    {
        return this._defaultService.GetCosSign();
    }

    /// <summary>
    /// 获取临时密钥
    /// </summary>
    /// <param name="fileName">文件名</param>
    /// <param name="modelName">模型名称</param>
    /// <returns></returns>
    [HttpGet]
    [ActionDescriptor(DisplayName = "获取COS临时密钥")]
    public GenerateTemporaryModel GetGenerateTemporaryKey(string fileName = "", string modelName = "")
    {
        return this._defaultService.GetGenerateTemporaryKey(fileName, modelName);
    }
}