Commit addd9f4c79a923291d29cbc30b30003d8d5b62d1

Authored by “wangming”
1 parent 33599f59

feat: 配置annexpic接口使用阿里云OSS存储

- 创建annexpic_bak备份接口
- 配置阿里云OSS存储(成都区域)
- annexpic接口强制使用阿里云OSS,不受全局FileStoreType配置影响
- 修复OSS路径格式问题(使用正斜杠)
- 更新BucketName为lvqian-erip
netcore/src/Application/NCC.API.Core/Startup.cs
@@ -106,6 +106,38 @@ namespace NCC.API.Core @@ -106,6 +106,38 @@ namespace NCC.API.Core
106 106
107 #endregion 107 #endregion
108 108
  109 + #region 阿里云OSS
  110 +
  111 + var aliyunOSSEndpoint = App.Configuration["NCC_App:AliyunOSS:Endpoint"];
  112 + var aliyunOSSAccessKey = App.Configuration["NCC_App:AliyunOSS:AccessKeyId"];
  113 + var aliyunOSSSecretKey = App.Configuration["NCC_App:AliyunOSS:AccessKeySecret"];
  114 + var aliyunOSSRegion = App.Configuration["NCC_App:AliyunOSS:Region"];
  115 + var bucketName = App.Configuration["NCC_App:BucketName"];
  116 +
  117 + if (!string.IsNullOrEmpty(aliyunOSSEndpoint) && !string.IsNullOrEmpty(aliyunOSSAccessKey) && !string.IsNullOrEmpty(aliyunOSSSecretKey))
  118 + {
  119 + services.AddOSSService("aliyun", option =>
  120 + {
  121 + option.Provider = OSSProvider.Aliyun;
  122 + // 阿里云OSS Endpoint格式:oss-{region}.aliyuncs.com
  123 + // 注意:不要包含bucket名称,只需要区域Endpoint
  124 + option.Endpoint = aliyunOSSEndpoint;
  125 + option.AccessKey = aliyunOSSAccessKey;
  126 + option.SecretKey = aliyunOSSSecretKey;
  127 + option.IsEnableHttps = true;
  128 + option.IsEnableCache = true;
  129 + // 对于OnceMi.AspNetCore.OSS,Region是可选的
  130 + // 但如果出现Endpoint不匹配错误,可能需要明确设置Region
  131 + // 成都区域的Region格式:cn-chengdu
  132 + if (!string.IsNullOrEmpty(aliyunOSSRegion))
  133 + {
  134 + option.Region = aliyunOSSRegion;
  135 + }
  136 + });
  137 + }
  138 +
  139 + #endregion
  140 +
109 #region 微信 141 #region 微信
110 services.AddSenparcGlobalServices(App.Configuration)//Senparc.CO2NET 全局注册 142 services.AddSenparcGlobalServices(App.Configuration)//Senparc.CO2NET 全局注册
111 .AddSenparcWeixinServices(App.Configuration);//Senparc.Weixin 注册(如果使用Senparc.Weixin SDK则添加) 143 .AddSenparcWeixinServices(App.Configuration);//Senparc.Weixin 注册(如果使用Senparc.Weixin SDK则添加)
netcore/src/Application/NCC.API/appsettings.json
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
33 "DBType": "MySql", //MySql;SqlServer;Oracle;PostgreSQL;Dm;Kdbndp; 33 "DBType": "MySql", //MySql;SqlServer;Oracle;PostgreSQL;Dm;Kdbndp;
34 //SqlServer 34 //SqlServer
35 // "DefaultConnection": "Data Source=localhost;Initial Catalog={0};User ID=sqladmin;Password=P@ssw0rd;MultipleActiveResultSets=true" 35 // "DefaultConnection": "Data Source=localhost;Initial Catalog={0};User ID=sqladmin;Password=P@ssw0rd;MultipleActiveResultSets=true"
36 - "DefaultConnection": "Database={0};Data Source=rm-bp19ohrgc6111ynzh1o.mysql.rds.aliyuncs.com;Port=3306;User Id=netteam;Password=netteam;Charset=utf8;TreatTinyAsBoolean=true;" 36 + "DefaultConnection": "Database={0};Data Source=rm-2vccze142rc9a8f58bo.mysql.cn-chengdu.rds.aliyuncs.com;Port=3306;User Id=lvqiansql;Password=LvQ1@n!20251211;Charset=utf8;TreatTinyAsBoolean=true;"
37 37
38 }, 38 },
39 "SpecificationDocumentSettings": { 39 "SpecificationDocumentSettings": {
@@ -204,9 +204,16 @@ @@ -204,9 +204,16 @@
204 "domainKey": "yozoHbiPMzu50374" 204 "domainKey": "yozoHbiPMzu50374"
205 }, 205 },
206 //Minio 206 //Minio
207 - "BucketName": "NCCsoftoss", 207 + "BucketName": "lvqian-erip",
208 //文件存储类型(本地:local,MinIo:minio,阿里云:aliyun-oss,腾讯云:tencent-cos) 208 //文件存储类型(本地:local,MinIo:minio,阿里云:aliyun-oss,腾讯云:tencent-cos)
209 "FileStoreType": "local", 209 "FileStoreType": "local",
  210 + //阿里云OSS配置
  211 + "AliyunOSS": {
  212 + "AccessKeyId": "LTAI5t6h4i95uapwzDwKfNxi",
  213 + "AccessKeySecret": "84dpUAlu2eoyFOIEhFGkZlIy45h0B6",
  214 + "Endpoint": "oss-cn-chengdu.aliyuncs.com",
  215 + "Region": "cn-chengdu"
  216 + },
210 //================== 系统错误邮件报告反馈相关 ============================== --> 217 //================== 系统错误邮件报告反馈相关 ============================== -->
211 //软件的错误报告 218 //软件的错误报告
212 "ErrorReport": "false", 219 "ErrorReport": "false",
netcore/src/Modularity/System/NCC.System/Service/Common/FileService.cs
@@ -64,7 +64,11 @@ namespace NCC.System.Service.Common @@ -64,7 +64,11 @@ namespace NCC.System.Service.Common
64 throw NCCException.Oh(ErrorCode.D1800); 64 throw NCCException.Oh(ErrorCode.D1800);
65 var _filePath = GetPathByType(type); 65 var _filePath = GetPathByType(type);
66 var _fileName = DateTime.Now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + Path.GetExtension(file.FileName); 66 var _fileName = DateTime.Now.ToString("yyyyMMdd") + "_" + YitIdHelper.NextId().ToString() + Path.GetExtension(file.FileName);
67 - await UploadFileByType(file, _filePath, _fileName); 67 +
  68 + // annexpic 类型强制使用阿里云OSS存储
  69 + string forceStoreType = type == "annexpic" ? "aliyun-oss" : null;
  70 + await UploadFileByType(file, _filePath, _fileName, forceStoreType);
  71 +
68 return new { name = _fileName, url = string.Format("/api/File/Image/{0}/{1}", type, _fileName) }; 72 return new { name = _fileName, url = string.Format("/api/File/Image/{0}/{1}", type, _fileName) };
69 } 73 }
70 74
@@ -79,7 +83,9 @@ namespace NCC.System.Service.Common @@ -79,7 +83,9 @@ namespace NCC.System.Service.Common
79 public async Task<IActionResult> GetImg(string type, string fileName) 83 public async Task<IActionResult> GetImg(string type, string fileName)
80 { 84 {
81 var filePath = Path.Combine(GetPathByType(type), fileName.Replace("@", ".")); 85 var filePath = Path.Combine(GetPathByType(type), fileName.Replace("@", "."));
82 - return await DownloadFileByType(filePath, fileName); 86 + // annexpic 类型强制使用阿里云OSS存储
  87 + string forceStoreType = type == "annexpic" ? "aliyun-oss" : null;
  88 + return await DownloadFileByType(filePath, fileName, forceStoreType);
83 //return new FileStreamResult(new FileStream(filePath, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName }; 89 //return new FileStreamResult(new FileStream(filePath, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
84 } 90 }
85 91
@@ -181,7 +187,9 @@ namespace NCC.System.Service.Common @@ -181,7 +187,9 @@ namespace NCC.System.Service.Common
181 fileDownloadName = Path.GetFileName(filePath); 187 fileDownloadName = Path.GetFileName(filePath);
182 if (fileDownloadName.IsNullOrWhiteSpace()) 188 if (fileDownloadName.IsNullOrWhiteSpace())
183 fileDownloadName = fileName.Replace(GetPathByType(type), ""); 189 fileDownloadName = fileName.Replace(GetPathByType(type), "");
184 - return await DownloadFileByType(filePath, fileDownloadName); 190 + // annexpic 类型强制使用阿里云OSS存储
  191 + string forceStoreType = type == "annexpic" ? "aliyun-oss" : null;
  192 + return await DownloadFileByType(filePath, fileDownloadName, forceStoreType);
185 } 193 }
186 else 194 else
187 { 195 {
@@ -198,15 +206,19 @@ namespace NCC.System.Service.Common @@ -198,15 +206,19 @@ namespace NCC.System.Service.Common
198 /// <param name="file"></param> 206 /// <param name="file"></param>
199 /// <param name="filePath"></param> 207 /// <param name="filePath"></param>
200 /// <param name="fileName"></param> 208 /// <param name="fileName"></param>
  209 + /// <param name="forceStoreType">强制使用指定的存储类型(如:aliyun-oss),如果为空则使用配置的存储类型</param>
201 /// <returns></returns> 210 /// <returns></returns>
202 [NonAction] 211 [NonAction]
203 - public async Task UploadFileByType(IFormFile file, string filePath, string fileName) 212 + public async Task UploadFileByType(IFormFile file, string filePath, string fileName, string forceStoreType = null)
204 { 213 {
205 try 214 try
206 { 215 {
207 var bucketName = KeyVariable.BucketName; 216 var bucketName = KeyVariable.BucketName;
208 - var fileStoreType = KeyVariable.FileStoreType;  
209 - var uploadPath = Path.Combine(filePath, fileName); 217 + var fileStoreType = !string.IsNullOrEmpty(forceStoreType) ? forceStoreType : KeyVariable.FileStoreType;
  218 + // OSS路径使用正斜杠,不使用Path.Combine
  219 + var uploadPath = fileStoreType == "aliyun-oss" || fileStoreType == "tencent-cos" || fileStoreType == "minio"
  220 + ? $"{filePath.TrimEnd('/').TrimEnd('\\')}/{fileName}"
  221 + : Path.Combine(filePath, fileName);
210 var stream = file.OpenReadStream(); 222 var stream = file.OpenReadStream();
211 switch (fileStoreType) 223 switch (fileStoreType)
212 { 224 {
@@ -229,9 +241,16 @@ namespace NCC.System.Service.Common @@ -229,9 +241,16 @@ namespace NCC.System.Service.Common
229 break; 241 break;
230 } 242 }
231 } 243 }
232 - catch (Exception) 244 + catch (Exception ex)
233 { 245 {
234 - throw NCCException.Oh(ErrorCode.D8003); 246 + // 记录详细错误信息以便调试
  247 + var errorMsg = $"文件上传失败: {ex.Message}";
  248 + if (ex.InnerException != null)
  249 + {
  250 + errorMsg += $", InnerException: {ex.InnerException.Message}";
  251 + }
  252 + // 抛出包含详细信息的异常
  253 + throw NCCException.Oh($"[D8003] {errorMsg}");
235 } 254 }
236 } 255 }
237 256
@@ -240,14 +259,15 @@ namespace NCC.System.Service.Common @@ -240,14 +259,15 @@ namespace NCC.System.Service.Common
240 /// </summary> 259 /// </summary>
241 /// <param name="filePath"></param> 260 /// <param name="filePath"></param>
242 /// <param name="fileDownLoadName"></param> 261 /// <param name="fileDownLoadName"></param>
  262 + /// <param name="forceStoreType">强制使用指定的存储类型(如:aliyun-oss),如果为空则使用配置的存储类型</param>
243 /// <returns></returns> 263 /// <returns></returns>
244 [NonAction] 264 [NonAction]
245 - public async Task<FileStreamResult> DownloadFileByType(string filePath, string fileDownLoadName) 265 + public async Task<FileStreamResult> DownloadFileByType(string filePath, string fileDownLoadName, string forceStoreType = null)
246 { 266 {
247 try 267 try
248 { 268 {
249 var bucketName = KeyVariable.BucketName; 269 var bucketName = KeyVariable.BucketName;
250 - var fileStoreType = KeyVariable.FileStoreType; 270 + var fileStoreType = !string.IsNullOrEmpty(forceStoreType) ? forceStoreType : KeyVariable.FileStoreType;
251 switch (fileStoreType) 271 switch (fileStoreType)
252 { 272 {
253 case "minio": 273 case "minio":
@@ -255,7 +275,7 @@ namespace NCC.System.Service.Common @@ -255,7 +275,7 @@ namespace NCC.System.Service.Common
255 var stream1 = await url1.GetAsStreamAsync(); 275 var stream1 = await url1.GetAsStreamAsync();
256 return new FileStreamResult(stream1, "application/octet-stream") { FileDownloadName = fileDownLoadName }; 276 return new FileStreamResult(stream1, "application/octet-stream") { FileDownloadName = fileDownLoadName };
257 case "aliyun-oss": 277 case "aliyun-oss":
258 - var url2 = await _oSSServiceFactory.Create("Aliyun").PresignedGetObjectAsync(bucketName, filePath, 86400); 278 + var url2 = await _oSSServiceFactory.Create("aliyun").PresignedGetObjectAsync(bucketName, filePath, 86400);
259 var stream2 = await url2.GetAsStreamAsync(); 279 var stream2 = await url2.GetAsStreamAsync();
260 return new FileStreamResult(stream2, "application/octet-stream") { FileDownloadName = fileDownLoadName }; 280 return new FileStreamResult(stream2, "application/octet-stream") { FileDownloadName = fileDownLoadName };
261 case "tencent-cos": 281 case "tencent-cos":
@@ -299,6 +319,8 @@ namespace NCC.System.Service.Common @@ -299,6 +319,8 @@ namespace NCC.System.Service.Common
299 return FileVariable.SystemFilePath; 319 return FileVariable.SystemFilePath;
300 case "annexpic": 320 case "annexpic":
301 return FileVariable.SystemFilePath; 321 return FileVariable.SystemFilePath;
  322 + case "annexpic_bak":
  323 + return FileVariable.SystemFilePath;
302 case "document": 324 case "document":
303 return FileVariable.DocumentFilePath; 325 return FileVariable.DocumentFilePath;
304 case "diskdocument": 326 case "diskdocument":