From 592de4deb4088ddfbea32a9cb47ee9b98d699dbe Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Mon, 18 May 2026 16:53:26 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=90=8C=E6=AD=A5=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IRC.Core.SCP/Service/OSSService.cs | 129 +++++++++++++++------ IRC.Core.SCP/Service/_MapConfig.cs | 16 +++ IRC.Core.SCP/appsettings.Test_IRC_SCP.json | 21 ++++ 3 files changed, 130 insertions(+), 36 deletions(-) create mode 100644 IRC.Core.SCP/Service/_MapConfig.cs diff --git a/IRC.Core.SCP/Service/OSSService.cs b/IRC.Core.SCP/Service/OSSService.cs index 962c86581..df4f5cb39 100644 --- a/IRC.Core.SCP/Service/OSSService.cs +++ b/IRC.Core.SCP/Service/OSSService.cs @@ -194,7 +194,7 @@ public interface IOSSService List GetRootFolderNames(); - public ObjectStoreDTO GetObjectStoreTempToken(string? domain = null, bool? isGetAllTempToken = null); + public ObjectStoreDTO GetObjectStoreTempToken(string? domain = null, bool? isGetAllTempToken = null, string? objectUse = null); public Task MoveObject(string sourcePath, string destPath, bool overwrite = true); @@ -1856,23 +1856,39 @@ public class OSSService(IOptionsMonitor options, - public ObjectStoreDTO GetObjectStoreTempToken(string? domain = null, bool? isGetAllTempToken = null) + public ObjectStoreDTO GetObjectStoreTempToken(string? domain = null, bool? isGetAllTempToken = null, string? objectUse = null) { - //如果传递了域名,并且打开了存储同步,根据域名使用的具体存储覆盖之前的配置,否则就用固定的配置 - if (ObjectStoreServiceOptions.IsOpenStoreSync && domain.IsNotNullOrEmpty()) + string objectStoreUse = string.Empty; + //使用指定配置 + if (objectUse != null) { - var userDomain = domain?.Trim(); - - var find = ObjectStoreServiceOptions.SyncConfigList.FirstOrDefault(t => t.Domain == userDomain); - if (find != null) + objectStoreUse = objectUse?.Trim() ?? string.Empty; + } + //根据域名动态判断 + else + { + //如果传递了域名,并且打开了存储同步,根据域名使用的具体存储覆盖之前的配置,否则就用固定的配置 + if (ObjectStoreServiceOptions.IsOpenStoreSync && domain.IsNotNullOrEmpty() && ObjectStoreServiceOptions.SyncConfigList.Any(t => t.Domain == domain)) { - ObjectStoreServiceOptions.ObjectStoreUse = find.Primary; + + var find = ObjectStoreServiceOptions.SyncConfigList.FirstOrDefault(t => t.Domain == domain); + if (find != null) + { + objectStoreUse = find.Primary; + } + + } + else + { + //兜底,如果是本地测试环境,那就使用部署默认配置 + objectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse; } } - var objectStoreDTO = new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, IsOpenStoreSync = ObjectStoreServiceOptions.IsOpenStoreSync, SyncConfigList = ObjectStoreServiceOptions.SyncConfigList }; - if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS" || isGetAllTempToken == true) + var objectStoreDTO = new ObjectStoreDTO() { ObjectStoreUse = objectStoreUse, IsOpenStoreSync = ObjectStoreServiceOptions.IsOpenStoreSync, SyncConfigList = ObjectStoreServiceOptions.SyncConfigList }; + + if (objectStoreUse == "AliyunOSS" || isGetAllTempToken == true) { var ossOptions = ObjectStoreServiceOptions.AliyunOSS; @@ -1920,11 +1936,11 @@ public class OSSService(IOptionsMonitor options, objectStoreDTO.AliyunOSS = tempToken; } - else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + if (objectStoreUse == "MinIO") { objectStoreDTO.MinIO = ObjectStoreServiceOptions.MinIO; } - else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS" || isGetAllTempToken == true) + if (objectStoreUse == "AWS" || isGetAllTempToken == true) { var awsOptions = ObjectStoreServiceOptions.AWS; @@ -1969,7 +1985,8 @@ public class OSSService(IOptionsMonitor options, objectStoreDTO.AWS = tempToken; } - else + + if (objectStoreUse.IsNullOrEmpty()) { throw new BusinessValidationFailedException("未定义的存储介质类型"); } @@ -1980,7 +1997,7 @@ public class OSSService(IOptionsMonitor options, public async Task SyncFileAsync(string objectKey, ObjectStoreUse source, ObjectStoreUse destination, CancellationToken ct = default) { - GetObjectStoreTempToken(isGetAllTempToken: true); + var tempConfig = GetObjectStoreTempToken(isGetAllTempToken: true); var aliConfig = ObjectStoreServiceOptions.AliyunOSS; @@ -1999,36 +2016,76 @@ public class OSSService(IOptionsMonitor options, var amazonS3Client = new AmazonS3Client(credentials, clientConfig); + // ⭐ 关键变量 + IDisposable? owner = null; + Stream sourceStream; + long contentLength; - // 根据源选择流式下载 - Stream sourceStream = source switch + // ========= 获取流 + 长度 ========= + switch (source) { - ObjectStoreUse.AliyunOSS => _ossClient.GetObject(aliConfig.BucketName, objectKey).Content, - ObjectStoreUse.AWS => (await amazonS3Client.GetObjectAsync(awsConfig.BucketName, objectKey, ct)).ResponseStream, - _ => throw new BusinessValidationFailedException("未定义的同步类型") - }; + case ObjectStoreUse.AliyunOSS: + { - if (source == ObjectStoreUse.AliyunOSS) + var obj = _ossClient.GetObject( + aliConfig.BucketName, + objectKey); + + owner = obj; + sourceStream = obj.Content; + contentLength = obj.ContentLength; + break; + } + + case ObjectStoreUse.AWS: + { + var response = await amazonS3Client.GetObjectAsync( + awsConfig.BucketName, + objectKey, + ct); + + owner = response; + sourceStream = response.ResponseStream; + contentLength = response.Headers.ContentLength; + break; + } + + default: + throw new BusinessValidationFailedException("未定义的同步类型"); + } + + try { - var putRequest = new Amazon.S3.Model.PutObjectRequest + // ========= 上传 ========= + if (destination == ObjectStoreUse.AWS) { - BucketName = awsConfig.BucketName, - Key = objectKey, - InputStream = sourceStream - }; + var putRequest = new Amazon.S3.Model.PutObjectRequest + { + BucketName = awsConfig.BucketName, + Key = objectKey, + InputStream = sourceStream, + Headers = { ContentLength = contentLength } + }; - await amazonS3Client.PutObjectAsync(putRequest, ct); + await amazonS3Client.PutObjectAsync(putRequest, ct); + } + else if (destination == ObjectStoreUse.AliyunOSS) + { + _ossClient.PutObject( + aliConfig.BucketName, + objectKey, + sourceStream); + } + else + { + throw new BusinessValidationFailedException("未定义的同步类型"); + } } - else if (source == ObjectStoreUse.AWS) + finally { - _ossClient.PutObject(aliConfig.BucketName, objectKey, sourceStream); + // ⭐⭐⭐ 真正释放 HTTP 连接 + owner?.Dispose(); } - else - { - throw new BusinessValidationFailedException("未定义的同步类型"); - } - - await sourceStream.DisposeAsync(); // 释放流 } } diff --git a/IRC.Core.SCP/Service/_MapConfig.cs b/IRC.Core.SCP/Service/_MapConfig.cs new file mode 100644 index 000000000..d61e18c25 --- /dev/null +++ b/IRC.Core.SCP/Service/_MapConfig.cs @@ -0,0 +1,16 @@ +using AutoMapper; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.SCP.Service; + + +namespace IRaCIS.Core.Application.Service +{ + public class CommonConfig : Profile + { + public CommonConfig() + { + CreateMap().ReverseMap(); + } + } + +} diff --git a/IRC.Core.SCP/appsettings.Test_IRC_SCP.json b/IRC.Core.SCP/appsettings.Test_IRC_SCP.json index f2415cb51..289de42a4 100644 --- a/IRC.Core.SCP/appsettings.Test_IRC_SCP.json +++ b/IRC.Core.SCP/appsettings.Test_IRC_SCP.json @@ -50,6 +50,27 @@ "secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy", "bucketName": "hir-test", "viewEndpoint": "http://106.14.89.110:9001/hir-test/" + }, + // AWS S3 对象存储服务的配置 + "AWS": { + // AWS S3 的Region + "Region": "us-east-1", + // AWS S3 的内部访问端点 + "EndPoint": "s3.us-east-1.amazonaws.com", + // 是否使用 SSL + "UseSSL": true, + // AWS S3 的角色 ARN + "RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access", + // AWS S3 的访问密钥 ID + "AccessKeyId": "AKIAW3MEAFJX7IPXISP4", + // AWS S3 的访问密钥 Secret + "SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc", + // AWS S3 的Bucket名称 + "BucketName": "ei-med-s3-lili-uat-store", + // AWS S3 的访问端点 + "ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com", + // AWS S3 的持续数秒 + "DurationSeconds": 7200 } },