You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:31:19