修改项目停止,归档
parent
d4e79e0905
commit
8eb31d0c81
|
|
@ -143,9 +143,8 @@ public enum ObjectStoreUse
|
|||
|
||||
public interface IOSSService
|
||||
{
|
||||
public void SetImmediateArchiveRule(string prefix,
|
||||
StorageClass targetStorageClass,
|
||||
string ruleId = "immediate-archive");
|
||||
public Task SetImmediateArchiveRule(string prefix, string ruleId = "immediate-archive", bool isDelete = false);
|
||||
|
||||
|
||||
|
||||
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true);
|
||||
|
|
@ -190,183 +189,576 @@ public class OSSService : IOSSService
|
|||
|
||||
/// <summary>
|
||||
/// 将指定前缀下的所有现有文件立即转为目标存储类型
|
||||
/// 核心:Days = 0 表示对所有存量文件立即生效
|
||||
/// </summary>
|
||||
/// <param name="prefix">要转换的文件前缀,如 "project-a/logs/"</param>
|
||||
/// <param name="targetStorageClass">目标存储类型</param>
|
||||
/// <param name="ruleId">规则ID,默认为"immediate-archive"</param>
|
||||
public void SetImmediateArchiveRule(string prefix,
|
||||
StorageClass targetStorageClass,
|
||||
string ruleId = "immediate-archive")
|
||||
/// <param name="isDelete">默认是添加/更新 </param>
|
||||
public async Task SetImmediateArchiveRule(string prefix, string ruleId = "immediate-archive", bool isDelete = false)
|
||||
{
|
||||
|
||||
BackBatchGetToken();
|
||||
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
try
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
// 1. 先获取现有的所有生命周期规则(避免覆盖)
|
||||
var existingRules = new List<Aliyun.OSS.LifecycleRule>();
|
||||
|
||||
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
try
|
||||
{
|
||||
var existingRuleList = _ossClient.GetBucketLifecycle(aliConfig.BucketName);
|
||||
if (existingRuleList != null)
|
||||
// 1. 先获取现有的所有生命周期规则(避免覆盖)
|
||||
var existingRules = new List<Aliyun.OSS.LifecycleRule>();
|
||||
try
|
||||
{
|
||||
existingRules.AddRange(existingRuleList);
|
||||
Console.WriteLine($"找到 {existingRules.Count} 条现有规则");
|
||||
var existingRuleList = _ossClient.GetBucketLifecycle(aliConfig.BucketName);
|
||||
if (existingRuleList != null)
|
||||
{
|
||||
existingRules.AddRange(existingRuleList);
|
||||
Console.WriteLine($"找到 {existingRules.Count} 条现有规则");
|
||||
}
|
||||
}
|
||||
catch (OssException ex) when (ex.ErrorCode == "NoSuchLifecycle")
|
||||
{
|
||||
// 如果没有生命周期规则,继续创建新规则
|
||||
Console.WriteLine("当前Bucket无生命周期规则,将创建新规则");
|
||||
}
|
||||
|
||||
// 2. 创建立即生效的转换规则
|
||||
|
||||
ruleId = $"{ruleId}_{prefix}";
|
||||
var immediateRule = new Aliyun.OSS.LifecycleRule
|
||||
{
|
||||
ID = ruleId,
|
||||
Prefix = prefix,
|
||||
Status = RuleStatus.Enabled,
|
||||
Transitions = new Aliyun.OSS.LifecycleRule.LifeCycleTransition[]
|
||||
{
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 1
|
||||
},
|
||||
StorageClass = StorageClass.IA
|
||||
},
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 30
|
||||
},
|
||||
StorageClass = StorageClass.Archive
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 3. 移除同名的旧规则(如果存在)
|
||||
existingRules.RemoveAll(r => r.ID == ruleId);
|
||||
|
||||
// 4. 添加新规则到规则列表
|
||||
if (isDelete == false)
|
||||
{
|
||||
existingRules.Add(immediateRule);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
var request = new SetBucketLifecycleRequest(aliConfig.BucketName)
|
||||
{
|
||||
LifecycleRules = existingRules
|
||||
};
|
||||
|
||||
|
||||
_ossClient.SetBucketLifecycle(request);
|
||||
|
||||
|
||||
}
|
||||
catch (OssException ex)
|
||||
{
|
||||
Log.Logger.Error($"❌ 设置失败 [错误码: {ex.ErrorCode}] 详细: {ex.Message}");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"❌ 发生未知错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||
|
||||
|
||||
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
// 1. 获取现有的生命周期配置(避免覆盖)
|
||||
LifecycleConfiguration existingConfig = null;
|
||||
|
||||
var getRequest = new GetLifecycleConfigurationRequest { BucketName = awsConfig.BucketName };
|
||||
var response = await amazonS3Client.GetLifecycleConfigurationAsync(getRequest);
|
||||
existingConfig = response.Configuration;
|
||||
Console.WriteLine($"找到 {existingConfig?.Rules?.Count ?? 0} 条现有规则");
|
||||
|
||||
// 2. 生成唯一的规则ID
|
||||
ruleId = $"{ruleId}_{prefix.Replace('/', '_').Trim('_')}";
|
||||
|
||||
// 3. 创建新的生命周期规则
|
||||
var immediateRule = new Amazon.S3.Model.LifecycleRule
|
||||
{
|
||||
Id = ruleId,
|
||||
Filter = new LifecycleFilter
|
||||
{
|
||||
// 使用前缀筛选对象
|
||||
LifecycleFilterPredicate = new LifecyclePrefixPredicate { Prefix = prefix }
|
||||
},
|
||||
Status = LifecycleRuleStatus.Enabled,
|
||||
// 定义多个转换阶段
|
||||
Transitions = new List<LifecycleTransition>
|
||||
{
|
||||
// 1天后转为低频访问 (Standard-IA)
|
||||
new LifecycleTransition
|
||||
{
|
||||
Days = 1,
|
||||
StorageClass = S3StorageClass.StandardInfrequentAccess // 对应S3 Standard-IA
|
||||
},
|
||||
// 30天后转为归档 (Glacier Instant Retrieval)
|
||||
new LifecycleTransition
|
||||
{
|
||||
Days = 30,
|
||||
StorageClass = S3StorageClass.GlacierInstantRetrieval // 对应归档(即时检索)
|
||||
}
|
||||
// 如果需要更深的归档,可以继续添加:
|
||||
// new LifecycleTransition { Days = 90, StorageClass = S3StorageClass.GlacierFlexibleRetrieval },
|
||||
// new LifecycleTransition { Days = 180, StorageClass = S3StorageClass.DeepArchive }
|
||||
}
|
||||
// 注意:S3的生命周期规则不支持设置“立即生效(Days=0)”。
|
||||
// 如果要对存量文件立即生效,需要配合其他方法(如批量修改存储类型)。
|
||||
};
|
||||
|
||||
// 4. 更新规则列表(移除同名旧规则,添加新规则)
|
||||
var existingRules = existingConfig.Rules ?? new List<Amazon.S3.Model.LifecycleRule>();
|
||||
existingRules.RemoveAll(r => r.Id == ruleId);
|
||||
|
||||
if (isDelete == false)
|
||||
{
|
||||
existingRules.Add(immediateRule);
|
||||
|
||||
}
|
||||
|
||||
// 5. 提交新的生命周期配置
|
||||
var putRequest = new PutLifecycleConfigurationRequest
|
||||
{
|
||||
BucketName = awsConfig.BucketName,
|
||||
Configuration = new LifecycleConfiguration { Rules = existingRules }
|
||||
};
|
||||
|
||||
await amazonS3Client.PutLifecycleConfigurationAsync(putRequest);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
|
||||
/// 解冻指定前缀下的所有归档/冷归档文件
|
||||
/// </summary>
|
||||
/// <param name="prefix">要解冻的文件前缀</param>
|
||||
/// <param name="restoreDays">解冻后文件保持可读的天数(默认3天)</param>
|
||||
/// <param name="restoreTier">解冻优先级(仅AWS有效)</param>
|
||||
/// <param name="batchSize">批量处理大小(默认100)</param>
|
||||
public async Task RestoreFilesByPrefixAsync(string prefix,
|
||||
int restoreDays = 3,
|
||||
string restoreTier = "Standard",
|
||||
int batchSize = 100)
|
||||
{
|
||||
BackBatchGetToken();
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
var client = new OssClient(
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
|
||||
AliyunOSSTempToken.AccessKeyId,
|
||||
AliyunOSSTempToken.AccessKeySecret,
|
||||
AliyunOSSTempToken.SecurityToken
|
||||
);
|
||||
|
||||
var bucketName = aliConfig.BucketName;
|
||||
int totalRestored = 0;
|
||||
int totalSkipped = 0;
|
||||
int totalFailed = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"开始解冻阿里云OSS文件,前缀: {prefix}");
|
||||
|
||||
// 1. 分页列举文件
|
||||
string nextMarker = null;
|
||||
ObjectListing result = null;
|
||||
|
||||
do
|
||||
{
|
||||
var listRequest = new Aliyun.OSS.ListObjectsRequest(bucketName)
|
||||
{
|
||||
Prefix = prefix,
|
||||
Marker = nextMarker,
|
||||
MaxKeys = batchSize
|
||||
};
|
||||
|
||||
result = client.ListObjects(listRequest);
|
||||
|
||||
// 2. 批量处理每个文件
|
||||
var restoreTasks = new List<Task>();
|
||||
|
||||
foreach (var obj in result.ObjectSummaries)
|
||||
{
|
||||
// 跳过非归档/冷归档文件
|
||||
if (obj.StorageClass != StorageClass.Archive.ToString())
|
||||
{
|
||||
Console.WriteLine($"跳过非归档文件: {obj.Key} (类型: {obj.StorageClass})");
|
||||
totalSkipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 提交解冻任务
|
||||
var task = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var restoreRequest = new Aliyun.OSS.RestoreObjectRequest(bucketName, obj.Key)
|
||||
{
|
||||
Days = restoreDays
|
||||
};
|
||||
|
||||
client.RestoreObject(restoreRequest);
|
||||
|
||||
Interlocked.Increment(ref totalRestored);
|
||||
Console.WriteLine($"✅ 已提交解冻: {obj.Key} (天数: {restoreDays})");
|
||||
}
|
||||
catch (OssException ex)
|
||||
{
|
||||
if (ex.ErrorCode == "RestoreAlreadyInProgress")
|
||||
{
|
||||
Console.WriteLine($"⚠️ 解冻已在进行中: {obj.Key}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Interlocked.Increment(ref totalFailed);
|
||||
Console.WriteLine($"❌ 解冻失败 [{obj.Key}]: {ex.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Interlocked.Increment(ref totalFailed);
|
||||
Console.WriteLine($"❌ 解冻失败 [{obj.Key}]: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
restoreTasks.Add(task);
|
||||
|
||||
// 控制并发,避免请求过快
|
||||
if (restoreTasks.Count >= 10) // 并发10个任务
|
||||
{
|
||||
await Task.WhenAll(restoreTasks);
|
||||
restoreTasks.Clear();
|
||||
await Task.Delay(100); // 短暂延迟
|
||||
}
|
||||
}
|
||||
|
||||
// 等待剩余任务完成
|
||||
if (restoreTasks.Count > 0)
|
||||
{
|
||||
await Task.WhenAll(restoreTasks);
|
||||
}
|
||||
|
||||
nextMarker = result.NextMarker;
|
||||
|
||||
} while (result.IsTruncated);
|
||||
|
||||
// 3. 输出统计结果
|
||||
Console.WriteLine("\n================ 解冻完成 ================");
|
||||
Console.WriteLine($"总计处理: {totalRestored + totalSkipped + totalFailed} 个文件");
|
||||
Console.WriteLine($"成功解冻: {totalRestored} 个");
|
||||
Console.WriteLine($"跳过文件: {totalSkipped} 个 (非归档类型)");
|
||||
Console.WriteLine($"解冻失败: {totalFailed} 个");
|
||||
|
||||
if (totalRestored > 0)
|
||||
{
|
||||
Console.WriteLine($"\n📋 解冻说明:");
|
||||
Console.WriteLine($" • 解冻任务已提交,文件将在后台处理");
|
||||
Console.WriteLine($" • 解冻完成后,文件将保持可读状态 {restoreDays} 天");
|
||||
Console.WriteLine($" • 归档文件约需1分钟,冷归档需数小时");
|
||||
}
|
||||
}
|
||||
catch (OssException ex) when (ex.ErrorCode == "NoSuchLifecycle")
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果没有生命周期规则,继续创建新规则
|
||||
Console.WriteLine("当前Bucket无生命周期规则,将创建新规则");
|
||||
Log.Logger.Error($"❌ 阿里云解冻操作失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
var credentials = new SessionAWSCredentials(
|
||||
AWSTempToken.AccessKeyId,
|
||||
AWSTempToken.SecretAccessKey,
|
||||
AWSTempToken.SessionToken
|
||||
);
|
||||
|
||||
// 2. 创建立即生效的转换规则
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.GetBySystemName(awsConfig.Region),
|
||||
UseHttp = true,
|
||||
};
|
||||
|
||||
using var client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
var bucketName = awsConfig.BucketName;
|
||||
int totalRestored = 0;
|
||||
int totalSkipped = 0;
|
||||
int totalFailed = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"开始解冻AWS S3文件,前缀: {prefix}");
|
||||
|
||||
// 1. 分页列举文件
|
||||
string continuationToken = null;
|
||||
ListObjectsV2Response response = null;
|
||||
|
||||
do
|
||||
{
|
||||
var listRequest = new ListObjectsV2Request
|
||||
{
|
||||
BucketName = bucketName,
|
||||
Prefix = prefix,
|
||||
ContinuationToken = continuationToken,
|
||||
MaxKeys = batchSize
|
||||
};
|
||||
|
||||
response = await client.ListObjectsV2Async(listRequest);
|
||||
|
||||
// 2. 批量处理每个文件
|
||||
var restoreTasks = new List<Task>();
|
||||
|
||||
foreach (var obj in response.S3Objects)
|
||||
{
|
||||
// 需要先获取对象元数据以确定存储类型
|
||||
var metadataRequest = new Amazon.S3.Model.GetObjectMetadataRequest
|
||||
{
|
||||
BucketName = bucketName,
|
||||
Key = obj.Key
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
var metadata = await client.GetObjectMetadataAsync(metadataRequest);
|
||||
|
||||
// 检查是否为归档类型
|
||||
bool isGlacierObject = metadata.StorageClass == S3StorageClass.Glacier;
|
||||
|
||||
if (!isGlacierObject)
|
||||
{
|
||||
Console.WriteLine($"跳过非归档文件: {obj.Key} (类型: {metadata.StorageClass})");
|
||||
totalSkipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 提交解冻任务
|
||||
var task = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var restoreRequest = new Amazon.S3.Model.RestoreObjectRequest
|
||||
{
|
||||
BucketName = bucketName,
|
||||
Key = obj.Key,
|
||||
Days = restoreDays
|
||||
};
|
||||
|
||||
await client.RestoreObjectAsync(restoreRequest);
|
||||
|
||||
Interlocked.Increment(ref totalRestored);
|
||||
Console.WriteLine($"✅ 已提交解冻: {obj.Key} (天数: {restoreDays}, 优先级: {restoreTier})");
|
||||
}
|
||||
catch (AmazonS3Exception ex)
|
||||
{
|
||||
if (ex.ErrorCode == "RestoreAlreadyInProgress")
|
||||
{
|
||||
Console.WriteLine($"⚠️ 解冻已在进行中: {obj.Key}");
|
||||
}
|
||||
else if (ex.ErrorCode == "AccessDenied")
|
||||
{
|
||||
Interlocked.Increment(ref totalFailed);
|
||||
Console.WriteLine($"❌ 权限不足 [{obj.Key}]: 需要 s3:RestoreObject 权限");
|
||||
}
|
||||
else
|
||||
{
|
||||
Interlocked.Increment(ref totalFailed);
|
||||
Console.WriteLine($"❌ 解冻失败 [{obj.Key}]: {ex.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Interlocked.Increment(ref totalFailed);
|
||||
Console.WriteLine($"❌ 解冻失败 [{obj.Key}]: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
restoreTasks.Add(task);
|
||||
|
||||
// 控制并发
|
||||
if (restoreTasks.Count >= 5) // AWS建议较低并发
|
||||
{
|
||||
await Task.WhenAll(restoreTasks);
|
||||
restoreTasks.Clear();
|
||||
await Task.Delay(200); // AWS有速率限制
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"❌ 获取元数据失败 [{obj.Key}]: {ex.Message}");
|
||||
totalFailed++;
|
||||
}
|
||||
}
|
||||
|
||||
// 等待剩余任务
|
||||
if (restoreTasks.Count > 0)
|
||||
{
|
||||
await Task.WhenAll(restoreTasks);
|
||||
}
|
||||
|
||||
continuationToken = response.NextContinuationToken;
|
||||
|
||||
} while (response.IsTruncated == true);
|
||||
|
||||
// 3. 输出统计结果
|
||||
Console.WriteLine("\n================ 解冻完成 ================");
|
||||
Console.WriteLine($"总计处理: {totalRestored + totalSkipped + totalFailed} 个文件");
|
||||
Console.WriteLine($"成功解冻: {totalRestored} 个");
|
||||
Console.WriteLine($"跳过文件: {totalSkipped} 个 (非归档类型)");
|
||||
Console.WriteLine($"解冻失败: {totalFailed} 个");
|
||||
|
||||
if (totalRestored > 0)
|
||||
{
|
||||
Console.WriteLine($"\n📋 AWS解冻说明:");
|
||||
Console.WriteLine($" • 解冻任务已提交到Glacier服务");
|
||||
Console.WriteLine($" • 标准解冻: 3-5小时 (Glacier Flexible Retrieval)");
|
||||
Console.WriteLine($" • 加急解冻: 1-5分钟 (额外收费)");
|
||||
Console.WriteLine($" • 解冻后文件可读 {restoreDays} 天");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Logger.Error($"❌ AWS解冻操作失败: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 坑方法,会清空之前的规则
|
||||
/// </summary>
|
||||
/// <param name="prefix"></param>
|
||||
/// <param name="ruleId"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||
public async Task SetLifecycle(string prefix, string ruleId = "immediate-archive")
|
||||
{
|
||||
BackBatchGetToken();
|
||||
|
||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
{
|
||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
ruleId = $"{ruleId}_{prefix}";
|
||||
var immediateRule = new Aliyun.OSS.LifecycleRule
|
||||
var rule = new Aliyun.OSS.LifecycleRule
|
||||
{
|
||||
ID = ruleId,
|
||||
Prefix = prefix,
|
||||
Status = RuleStatus.Enabled,
|
||||
Transitions = new Aliyun.OSS.LifecycleRule.LifeCycleTransition[]
|
||||
{
|
||||
{
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 1
|
||||
},
|
||||
StorageClass = targetStorageClass
|
||||
StorageClass = StorageClass.IA
|
||||
},
|
||||
new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
{
|
||||
|
||||
LifeCycleExpiration =
|
||||
{
|
||||
Days = 30
|
||||
},
|
||||
StorageClass = StorageClass.Archive
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 3. 移除同名的旧规则(如果存在)
|
||||
existingRules.RemoveAll(r => r.ID == ruleId);
|
||||
|
||||
// 4. 添加新规则到规则列表
|
||||
existingRules.Add(immediateRule);
|
||||
|
||||
|
||||
var request = new SetBucketLifecycleRequest(aliConfig.BucketName)
|
||||
{
|
||||
LifecycleRules= existingRules
|
||||
};
|
||||
|
||||
//会清空之前历史的规则,不能用。。。
|
||||
var request = new SetBucketLifecycleRequest(aliConfig.BucketName);
|
||||
request.AddLifecycleRule(rule);
|
||||
|
||||
_ossClient.SetBucketLifecycle(request);
|
||||
|
||||
Console.WriteLine("✅ 立即归档规则设置成功!");
|
||||
Console.WriteLine($" 规则ID: {ruleId}");
|
||||
Console.WriteLine($" 前缀: {prefix}");
|
||||
Console.WriteLine($" 目标存储类型: {targetStorageClass}");
|
||||
Console.WriteLine($" 生效时间: 将在下次生命周期扫描时生效(通常24小时内)");
|
||||
}
|
||||
catch (OssException ex)
|
||||
{
|
||||
Console.WriteLine($"❌ 设置失败 [错误码: {ex.ErrorCode}]");
|
||||
Console.WriteLine($" 详细: {ex.Message}");
|
||||
|
||||
// 处理特定错误
|
||||
if (ex.ErrorCode == "InvalidArgument")
|
||||
{
|
||||
Console.WriteLine(" 可能原因:存储类型不支持或参数格式错误");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
{
|
||||
Console.WriteLine($"❌ 发生未知错误: {ex.Message}");
|
||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||
|
||||
|
||||
|
||||
//提供awsEndPoint(域名)进行访问配置
|
||||
var clientConfig = new AmazonS3Config
|
||||
{
|
||||
RegionEndpoint = RegionEndpoint.USEast1,
|
||||
UseHttp = true,
|
||||
};
|
||||
|
||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public async Task SetLifecycle(string lifecycle)
|
||||
//{
|
||||
|
||||
|
||||
// if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||
// {
|
||||
// var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||
|
||||
// var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
||||
|
||||
|
||||
// var rule = new Aliyun.OSS.LifecycleRule
|
||||
// {
|
||||
// ID = "ArchiveOldFiles",
|
||||
// Prefix = "", // 全 bucket 生效
|
||||
// Status = RuleStatus.Enabled
|
||||
// };
|
||||
|
||||
// // 30 天转低频
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 30,
|
||||
// StorageClass = StorageClass.IA
|
||||
// });
|
||||
|
||||
// // 180 天转归档
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 180,
|
||||
// StorageClass = StorageClass.Archive
|
||||
// });
|
||||
|
||||
// // 365 天转冷归档
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 365,
|
||||
// StorageClass = StorageClass.ColdArchive
|
||||
// });
|
||||
|
||||
// // 730 天转深度归档
|
||||
// rule.Transitions.Add(new Aliyun.OSS.LifecycleRule.LifeCycleTransition
|
||||
// {
|
||||
// Days = 730,
|
||||
// StorageClass = StorageClass.DeepColdArchive
|
||||
// });
|
||||
|
||||
// var request = new SetBucketLifecycleRequest(aliConfig.BucketName);
|
||||
// request.AddLifecycleRule(rule);
|
||||
|
||||
// _ossClient.SetBucketLifecycle(request);
|
||||
|
||||
|
||||
// }
|
||||
// else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||
// {
|
||||
// var awsConfig = ObjectStoreServiceOptions.AWS;
|
||||
|
||||
// var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
||||
|
||||
|
||||
|
||||
// //提供awsEndPoint(域名)进行访问配置
|
||||
// var clientConfig = new AmazonS3Config
|
||||
// {
|
||||
// RegionEndpoint = RegionEndpoint.USEast1,
|
||||
// UseHttp = true,
|
||||
// };
|
||||
|
||||
// var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
||||
|
||||
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||
/// </summary>
|
||||
|
|
@ -705,7 +1097,7 @@ public class OSSService : IOSSService
|
|||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey)
|
||||
.WithSSL(minIOConfig.UseSSL)
|
||||
.Build();
|
||||
|
||||
|
||||
var pipe = new System.IO.Pipelines.Pipe();
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
|
|
|
|||
|
|
@ -15740,14 +15740,23 @@
|
|||
利用DocX 库 处理word国际化模板
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Helper.OSSService.SetImmediateArchiveRule(System.String,Aliyun.OSS.StorageClass,System.String)">
|
||||
<member name="M:IRaCIS.Core.Application.Helper.OSSService.SetImmediateArchiveRule(System.String,System.String,System.Boolean)">
|
||||
<summary>
|
||||
将指定前缀下的所有现有文件立即转为目标存储类型
|
||||
核心:Days = 0 表示对所有存量文件立即生效
|
||||
</summary>
|
||||
<param name="prefix">要转换的文件前缀,如 "project-a/logs/"</param>
|
||||
<param name="targetStorageClass">目标存储类型</param>
|
||||
<param name="ruleId">规则ID,默认为"immediate-archive"</param>
|
||||
<param name="isDelete">默认是添加/更新 </param>
|
||||
</member>
|
||||
<!-- Badly formed XML comment ignored for member "M:IRaCIS.Core.Application.Helper.OSSService.RestoreFilesByPrefixAsync(System.String,System.Int32,System.String,System.Int32)" -->
|
||||
<member name="M:IRaCIS.Core.Application.Helper.OSSService.SetLifecycle(System.String,System.String)">
|
||||
<summary>
|
||||
坑方法,会清空之前的规则
|
||||
</summary>
|
||||
<param name="prefix"></param>
|
||||
<param name="ruleId"></param>
|
||||
<returns></returns>
|
||||
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
||||
</member>
|
||||
<member name="M:IRaCIS.Core.Application.Helper.OSSService.UploadToOSSAsync(System.IO.Stream,System.String,System.String,System.Boolean)">
|
||||
<summary>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using IRaCIS.Application.Contracts;
|
||||
using Aliyun.OSS;
|
||||
using IRaCIS.Application.Contracts;
|
||||
using IRaCIS.Application.Interfaces;
|
||||
using IRaCIS.Core.Application.Contracts;
|
||||
using IRaCIS.Core.Application.Filter;
|
||||
|
|
@ -32,7 +33,7 @@ namespace IRaCIS.Core.Application
|
|||
IRepository<ClinicalDataTrialSet> _clinicalDataTrialSetRepository,
|
||||
IRepository<ReadingCriterionPage> _readingCriterionPageRepository,
|
||||
IRepository<SystemCriterionKeyFile> _systemCriterionKeyFileRepository,
|
||||
IOSSService oSSService,
|
||||
IOSSService _oSSService,
|
||||
IRepository<TrialCriterionKeyFile> _trialCriterionKeyFileRepository,
|
||||
IOrganInfoService _iOrganInfoService,
|
||||
IRepository<TrialBodyPart> _trialBodyPartRepository,
|
||||
|
|
@ -331,22 +332,22 @@ namespace IRaCIS.Core.Application
|
|||
public async Task SynchronizeKeyFile(Guid TrialReadingCriterionId)
|
||||
{
|
||||
var trialCriterion = await _readingQuestionCriterionTrialRepository.Where(x => x.Id == TrialReadingCriterionId).AsNoTracking().FirstOrDefaultAsync();
|
||||
if (trialCriterion != null && trialCriterion.ReadingQuestionCriterionSystemId!=null)
|
||||
if (trialCriterion != null && trialCriterion.ReadingQuestionCriterionSystemId != null)
|
||||
{
|
||||
var systemCriterionKeyFile = await _systemCriterionKeyFileRepository.Where(x => x.SystemCriterionId == trialCriterion.ReadingQuestionCriterionSystemId).ToListAsync();
|
||||
|
||||
List<TrialCriterionKeyFile> trialCriterionKeyFiles= new List<TrialCriterionKeyFile>();
|
||||
List<TrialCriterionKeyFile> trialCriterionKeyFiles = new List<TrialCriterionKeyFile>();
|
||||
|
||||
foreach (var item in systemCriterionKeyFile)
|
||||
foreach (var item in systemCriterionKeyFile)
|
||||
{
|
||||
|
||||
var path= await oSSService.UploadToOSSAsync(item.FilePath, $"{trialCriterion.TrialId}/ReadingModule/{trialCriterion.CriterionName}", true,true);
|
||||
var path = await _oSSService.UploadToOSSAsync(item.FilePath, $"{trialCriterion.TrialId}/ReadingModule/{trialCriterion.CriterionName}", true, true);
|
||||
|
||||
trialCriterionKeyFiles.Add(new TrialCriterionKeyFile
|
||||
{
|
||||
TrialCriterionId= TrialReadingCriterionId,
|
||||
FileName= item.FileName,
|
||||
FilePath= path
|
||||
TrialCriterionId = TrialReadingCriterionId,
|
||||
FileName = item.FileName,
|
||||
FilePath = path
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -1325,6 +1326,18 @@ namespace IRaCIS.Core.Application
|
|||
|
||||
await _trialRepository.BatchUpdateNoTrackingAsync(u => u.Id == trialId, s => new Trial { TrialFinishedTime = DateTime.Now });
|
||||
|
||||
|
||||
if (_readingQuestionCriterionTrialRepository.Any(t => t.IsSigned && t.ImageUploadEnum != ReadingImageUpload.None))
|
||||
{
|
||||
await _oSSService.SetImmediateArchiveRule($"{trial.Id}/Image/");
|
||||
await _oSSService.SetImmediateArchiveRule($"{trial.Id}/TaskImage/");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await _oSSService.SetImmediateArchiveRule($"{trial.Id}/Image/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await _fusionCache.SetAsync(CacheKeys.Trial(trial.Id.ToString()), trialStatusStr, TimeSpan.FromDays(7));
|
||||
|
|
@ -1546,7 +1559,7 @@ namespace IRaCIS.Core.Application
|
|||
[AllowAnonymous]
|
||||
public async Task<TrialConfigInfo> GetTrialExtralConfig(Guid trialId)
|
||||
{
|
||||
var extralObj = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.TrialExtraConfigJsonStr,t.IsExternalViewTrialChart, t.TrialObjectNameList, t.CollectImagesEnum, t.IsIQCAutoNextTask }).FirstOrDefault();
|
||||
var extralObj = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.TrialExtraConfigJsonStr, t.IsExternalViewTrialChart, t.TrialObjectNameList, t.CollectImagesEnum, t.IsIQCAutoNextTask }).FirstOrDefault();
|
||||
|
||||
var extralConfig = JsonConvert.DeserializeObject<TrialExtraConfig>(extralObj?.TrialExtraConfigJsonStr) ?? new TrialExtraConfig();
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ namespace IRaCIS.Core.Application.Service
|
|||
{
|
||||
if (storageClass == StorageClass.IA || storageClass == StorageClass.Archive || storageClass == StorageClass.ColdArchive || storageClass == StorageClass.DeepColdArchive)
|
||||
{
|
||||
_IOSSService.SetImmediateArchiveRule($"Test-Archive/Archive{(int)storageClass}", storageClass);
|
||||
_IOSSService.SetImmediateArchiveRule($"Test-Archive/Archive{(int)storageClass}/");
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue