diff --git a/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs b/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs
index 0745830..6251c01 100644
--- a/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs
+++ b/src/Modules/TakeoutSaaS.Module.Storage/Providers/S3StorageProviderBase.cs
@@ -60,6 +60,12 @@ public abstract class S3StorageProviderBase : IObjectStorageProvider, IDisposabl
///
public virtual async Task UploadAsync(StorageUploadRequest request, CancellationToken cancellationToken = default)
{
+ // 0. 兜底重置流位置,避免上游读取导致内容缺失
+ if (request.Content.CanSeek)
+ {
+ request.Content.Position = 0;
+ }
+
// 1. 构建上传请求
var putRequest = new PutObjectRequest
{
@@ -67,9 +73,18 @@ public abstract class S3StorageProviderBase : IObjectStorageProvider, IDisposabl
Key = request.ObjectKey,
InputStream = request.Content,
AutoCloseStream = false,
- ContentType = request.ContentType
+ ContentType = request.ContentType,
+ DisableDefaultChecksumValidation = true,
+ UseChunkEncoding = false,
+ DisablePayloadSigning = true
};
+ // 1.1 显式设置 Content-Length,避免 S3 SDK 对兼容网关使用 aws-chunked 导致对象内容被写入“chunk-signature”头而损坏
+ if (request.ContentLength > 0)
+ {
+ putRequest.Headers.ContentLength = request.ContentLength;
+ }
+
foreach (var kv in request.Metadata)
{
putRequest.Metadata[kv.Key] = kv.Value;
@@ -167,17 +182,50 @@ public abstract class S3StorageProviderBase : IObjectStorageProvider, IDisposabl
///
protected virtual IAmazonS3 CreateClient()
{
+ // 1. 尽量推断鉴权 Region(腾讯云 COS 的 S3 兼容域名形如:cos.ap-beijing.myqcloud.com)
+ var authenticationRegion = ResolveAuthenticationRegion(ServiceUrl);
+
+ // 2. 构建客户端配置
var config = new AmazonS3Config
{
ServiceURL = ServiceUrl,
ForcePathStyle = ForcePathStyle,
- UseHttp = !UseHttps
+ UseHttp = !UseHttps,
+ AuthenticationRegion = authenticationRegion
};
+ // 3. 创建客户端并返回
var credentials = new BasicAWSCredentials(AccessKey, SecretKey);
return new AmazonS3Client(credentials, config);
}
+ private static string? ResolveAuthenticationRegion(string serviceUrl)
+ {
+ // 1. 解析 URL
+ if (!Uri.TryCreate(serviceUrl, UriKind.Absolute, out var uri))
+ {
+ return null;
+ }
+
+ // 2. 提取 Host
+ var host = uri.Host;
+ if (string.IsNullOrWhiteSpace(host))
+ {
+ return null;
+ }
+
+ // 3. 按 COS 域名规则解析 Region
+ const string cosPrefix = "cos.";
+ const string cosSuffix = ".myqcloud.com";
+ if (host.StartsWith(cosPrefix, StringComparison.OrdinalIgnoreCase) &&
+ host.EndsWith(cosSuffix, StringComparison.OrdinalIgnoreCase))
+ {
+ var regionPart = host.Substring(cosPrefix.Length, host.Length - cosPrefix.Length - cosSuffix.Length);
+ return string.IsNullOrWhiteSpace(regionPart) ? null : regionPart;
+ }
+ return null;
+ }
+
private IAmazonS3 Client => _client ??= CreateClient();
///