AWS S3 CopyObjectAsync失败,密钥不存在,但获取/放置成功

时间:2018-07-07 01:18:04

标签: amazon-s3 aws-lambda

我为此奋斗了几个小时,却没有找到解决方案。这是场景:

var copyObjectRequest = new CopyObjectRequest
{
    SourceBucket  = s3Event.S3.Bucket.Name,
    SourceKey  = s3Event.S3.Object.Key,
    DestinationBucket  = OutputBucketName,
    DestinationKey  = s3Event.S3.Object.Key,
};

var deleteRequest = new DeleteObjectRequest
{
    BucketName = copyObjectRequest.SourceBucket,
    Key = copyObjectRequest.SourceKey,
};

await S3Client.CopyObjectAsync(copyObjectRequest);
await S3Client.DeleteObjectAsync(deleteRequest);

S3Client.CopyObjectAsync引发错误:“指定的键不存在。” (永远不会到达S3Client.DeleteObjectAsync。)

但是,以下代码可以工作(对于相同的值):

var request = new GetObjectRequest
{
    BucketName = copyObjectRequest.SourceBucket,
    Key = copyObjectRequest.SourceKey,
};

var response = await S3Client.GetObjectAsync(request);
var tempPath = $"{Guid.NewGuid():D}";
await response.WriteResponseStreamToFileAsync(tempPath, false, CancellationToken.None);

var putRequest = new PutObjectRequest
{
    BucketName = copyObjectRequest.DestinationBucket,
    Key = copyObjectRequest.DestinationKey,
    FilePath = tempPath,
};

var putResponse = await S3Client.PutObjectAsync(putRequest);

if (putResponse.HttpStatusCode == HttpStatusCode.OK)
{
    var deleteRequest = new DeleteObjectRequest
    {
        BucketName = copyObjectRequest.SourceBucket,
        Key = copyObjectRequest.SourceKey,
    };

    await S3Client.DeleteObjectAsync(deleteRequest);
}

为简洁起见,我删除了几乎所有错误检查,日志记录等,但是如果需要,我很乐意分享全部功能。

请注意,这是使用Core 2.0在C#Lambda函数中运行的。

  • 我已经排除了安全性,因为第二组调用需要与CopyObject调用相同的权限(我相信)(如果我错了,请纠正我)。
  • 毫无疑问,对象位于存储桶中,并且指定的键与第二组使用的结构完全相同。
  • 该密钥在目标存储桶中不存在。
  • 源存储桶和目标存储桶都具有相同的权限。
  • 键中没有特殊字符(我测试过的示例键是“ /US/ID/Teton/EC2ClientDemo.zip”和“ testkey”)。
  • 我正在测试的文件很小(该示例文件为30Kb)。
  • 我已经尝试过在CopyObjectRequest中使用CannedACL值,也没有使用CannedACL值的尝试(我认为出于我的目的,它不需要一个CannedACL值,它移动的所有文件都是私有的)。
  • 我已经确认所有存储桶都在同一地区(USWest2)。

我不知道为什么CopyObjectAsync失败。我曾尝试深入研究CopyObjectAsync的反汇编代码,但它的调用是如此间接,以至于我走得并不远。此时,我最好的猜测是这是CopyObjectAsync中的错误。

任何建议将不胜感激, 感谢您的阅读!

其他:我想明确地说,这可以从常规的AWSSDK库(CopyObjectAsync或CopyObject)运行,仅在Lambda环境中的Core 2.0异步调用CopyObjectAsync中失败。

1 个答案:

答案 0 :(得分:1)

好的,所以我弄清楚了,这绝对是核心2.0 CopyObjectAsync()中的一个错误。这是场景:

我们使用的开头带有斜杠的键,例如'/US/ID/Teton/EC2ClientDemo.zip'。当我打开S3日志记录(感谢@ Michael-sqlbot)时,我看到的是这样的:

[13/Jul/2018:17:44:18 +0000] 34.221.84.59 arn:aws:sts::434371411556:assumed-role/LambdaFunctionCreation/TestFunction 489A5570C2E840AC REST.COPY.OBJECT_GET US/ID/Teton/EC2ClientDemo.zip - 404 NoSuchKey

如您所见,在进行调用之前,CopyObjectAsync()函数删除了第一个斜杠。 Get,Put和Delete可以很好地处理这些特定键(我在非Core库中进行了测试,CopyObjectAsync()的同步和异步版本也可以很好地处理这些键)。

我要做的是修复以下问题:

var copyObjectRequest = new CopyObjectRequest
{
    SourceBucket  = s3Event.S3.Bucket.Name,
    SourceKey  = "/" + s3Event.S3.Object.Key,
    DestinationBucket  = OutputBucketName,
    DestinationKey  = "/" + s3Event.S3.Object.Key,
    CannedACL = S3CannedACL.BucketOwnerFullControl,
};

请注意在SourceKey和DestinationKey上添加了斜杠?没有这些键,就无法使用。

这是完整的最终代码:

var copyObjectRequest = new CopyObjectRequest
{
    SourceBucket = s3Event.S3.Bucket.Name,
    SourceKey = s3Event.S3.Object.Key,
    DestinationBucket = OutputBucketName,
    DestinationKey = s3Event.S3.Object.Key,
    CannedACL = S3CannedACL.BucketOwnerFullControl,
};

try
{
    await s3Client.CopyObjectAsync(copyObjectRequest);
}
catch (AmazonS3Exception ase) when (ase.Message.Contains("key does not exist"))
{
    try
    {
        // If this failed due to Key not found, then fix up the request for the CopyObjectAsync bug in the Core 2.0 library and try again.
        var patchedCopyObjectRequest = new CopyObjectRequest()
                                       {
                                           SourceBucket  = copyObjectRequest.SourceBucket,
                                           SourceKey  = "/" + copyObjectRequest.SourceKey,
                                           DestinationBucket  = copyObjectRequest.DestinationBucket,
                                           DestinationKey  = "/" + copyObjectRequest.DestinationKey,
                                           CannedACL = copyObjectRequest.CannedACL,
                                       };

        await s3Client.CopyObjectAsync(patchedCopyObjectRequest);
    }
    catch (AmazonS3Exception)
    {
        // Rethrow the initial exception, since we don't want a confusing message to contain the modified keys.
        throw ase;
    }
}