C#使用AWS SDK跨账号复制S3文件时内容损坏求助
问题原因与解决方案
问题根源
你的代码在处理二进制文件(如图片)时,错误地使用StreamReader读取内容并转为字符串,再通过ContentBody上传。StreamReader是文本读取器,会将二进制数据按默认编码(如UTF-8)解析为字符串,这会破坏二进制结构,导致文件大小变大、内容损坏。
最优解决方案:使用S3原生跨账号复制API
不需要下载再上传,直接调用S3的CopyObjectAsync,由AWS后台直接复制,效率更高且避免编码问题。需确保目标账号的S3桶策略允许源账号的IAM角色执行复制操作,或源账号角色有读取源桶权限、目标桶有写入权限。
修改后的代码:
public static async Task ListAndCopyObjects(string bucketName, string folderName, AmazonS3Client client) { Console.WriteLine("Listing Objects"); var listObjects = client.Paginators.ListObjectsV2(new ListObjectsV2Request() { BucketName = bucketName, Prefix = folderName, StartAfter = folderName }); Console.WriteLine("Loop through Files"); string destinationBucket = Environment.GetEnvironmentVariable("DESTINATION_BUCKET"); await foreach (var response in listObjects.Responses) { Console.WriteLine($"HttpStatusCode: {response.HttpStatusCode}"); Console.WriteLine($"Number of Keys: {response.KeyCount}"); foreach (var entry in response.S3Objects) { Console.WriteLine($"Key = {entry.Key} Size = {entry.Size}"); // 直接调用S3复制API await CopyS3Object(client, bucketName, entry.Key, destinationBucket, entry.Key); } } } private static async Task<bool> CopyS3Object(AmazonS3Client sourceClient, string sourceBucket, string sourceKey, string destBucket, string destKey) { try { var copyRequest = new CopyObjectRequest { SourceBucket = sourceBucket, SourceKey = sourceKey, DestinationBucket = destBucket, DestinationKey = destKey }; var response = await sourceClient.CopyObjectAsync(copyRequest); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"Successfully copied {sourceKey} to {destBucket}/{destKey}."); return true; } else { Console.WriteLine($"Could not copy {sourceKey} to {destBucket}/{destKey}."); return false; } } catch (AmazonS3Exception e) { Console.WriteLine("Error encountered ***. Message:'{0}' when copying object", e.Message); } catch (Exception e) { Console.WriteLine("Unknown error on server. Message:'{0}' when copying object", e.Message); } return false; }
若必须下载再上传的修复方案
如果因特殊需求需先下载再上传,需改用二进制流处理,避免文本编码:
// 修改DownloadObject方法,返回字节数组和ContentType private static async Task<(byte[] Content, string ContentType)> DownloadObject(AmazonS3Client client, string bucketName, string objectName) { Console.WriteLine("Downloading File"); try { GetObjectRequest request = new GetObjectRequest { BucketName = bucketName, Key = objectName }; using (GetObjectResponse response = await client.GetObjectAsync(request)) using (Stream responseStream = response.ResponseStream) { string contentType = response.Headers["Content-Type"]; Console.WriteLine("Content type: {0}", contentType); using (MemoryStream ms = new MemoryStream()) { await responseStream.CopyToAsync(ms); return (ms.ToArray(), contentType); } } } catch (AmazonS3Exception e) { Console.WriteLine("Error encountered ***. Message:'{0}' when downloading object", e.Message); } catch (Exception e) { Console.WriteLine("Unknown error on server. Message:'{0}' when downloading object", e.Message); } return (null, null); } // 修改UploadObject方法,接受字节数组 private static async Task<bool> UploadObject(AmazonS3Client client, string bucketName, string objectName, byte[] fileContent, string contentType) { if (fileContent == null) return false; var request = new PutObjectRequest { BucketName = bucketName, Key = objectName, InputStream = new MemoryStream(fileContent), ContentType = contentType }; var response = await client.PutObjectAsync(request); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine($"Successfully uploaded {objectName} to {bucketName}."); return true; } else { Console.WriteLine($"Could not upload {objectName} to {bucketName}."); return false; } } // 修改ListAndCopyObjects中的调用逻辑 foreach (var entry in response.S3Objects) { Console.WriteLine($"Key = {entry.Key} Size = {entry.Size}"); var (content, contentType) = await DownloadObject(client, bucketName, entry.Key); await UploadObject(destinationClient, Environment.GetEnvironmentVariable("DESTINATION_BUCKET"), entry.Key, content, contentType); }
关键权限配置说明
跨账号复制需确保权限正确:
- 源桶策略需允许目标账号的IAM角色/用户执行
s3:GetObject操作; - 目标桶策略需允许源账号的IAM角色/用户执行
s3:PutObject操作; - 或通过IAM角色赋予相应权限,避免桶策略过于宽泛。
内容的提问来源于stack exchange,提问作者Ela Buwa
相关产品推荐
相关产品推荐

