使用预签名URL将文件压缩到S3

时间:2012-04-11 05:17:24

标签: java curl amazon-s3

我整晚都在尝试使用Amazon S3预先签名的网址尝试PUT文件。我在java代码中生成预签名的URL。

    AWSCredentials credentials = new BasicAWSCredentials( accessKey, secretKey );
    client = new AmazonS3Client( credentials );
    GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest( bucketName, "myfilename", HttpMethod.PUT);
    request.setExpiration( new Date( System.currentTimeMillis() + (120 * 60 * 1000) ));
    return client.generatePresignedUrl( request ).toString();

然后,我想使用生成的预先签名的URL来使用curl PUT文件。

curl -v -H "content-type:image/jpg" -T mypicture.jpg https://mybucket.s3.amazonaws.com/myfilename?Expires=1334126943&AWSAccessKeyId=<accessKey>&Signature=<generatedSignature>

我认为,就像GET一样,这可以在一个非公开的存储桶上工作(这是预先签名的,对吗?)好吧,我在每次尝试时都被拒绝访问。最后,出于沮丧,我改变了桶的许可,允许每个人写。当然,预先签名的URL有效。我迅速从桶中删除了每个人的权限。现在,我无权删除通过我自己预先签名的URL上传到我的存储桶中的项目。我see now我可能应该在我上传的内容上加上x-amz-acl标题。我怀疑在我做对话之前我会创建几个不可修复的对象。

这导致了一些问题:

  • 如何使用PUT和生成的预先签名的URL上传curl?
  • 如何删除上传的文件和我创建的存储桶以进行测试?

最终目标是移动电话将使用此预先指定的URL来PUT图像。我试图将它作为概念证明进行卷曲。

更新:我在amazon forums上提出了一个问题。如果在那里提供答案,我会在这里作为答案。

2 个答案:

答案 0 :(得分:31)

这确实有点令人费解,我认为它是AWS SDK for Java中的一个错误(见下文) - 但首先,以下curl命令将上传您的文件(假设当然是更新的预签名网址:

curl -v -T mypicture.jpg https://mybucket.s3.amazonaws.com/myfilename?Expires=1334126943&AWSAccessKeyId=<accessKey>&Signature=<generatedSignature>

也就是说,我排除了Content type标题,结果产生了application/octet-stream(或binary/octet-stream),这显然是不可取的;因此,进一步挖掘已经有序。

背景/分析

PUT(和DELETE以及HEAD)对Amazon S3的预先签名的URL已知原则上有效,而不是在本网站的相关问题中证明的最少(请参阅我对{{3的回答) }})。

记录的辅助Upload to s3 with curl using pre-signed URL (getting 403)使用以下伪语法来说明查询字符串请求身份验证方法

StringToSign = HTTP-VERB + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Expires + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;    

它确实包含Content-Type标题,并且(正如您已经发现的那样)在某些记录的案例中这是一个缺失的部分,例如, AWS团队对Query String Request Authentication Alternative的响应,一旦添加就会产生有效的预签名网址。

使用GetPreSignedURL with PUT request确实很容易实现,这提供了便捷方法AWS SDK for .NET来做到这一点:

  

设置此请求的ContentType属性。此属性默认值   到“二进制/八位字节流”,但如果你需要别的东西,你可以   设置此属性。

因此,如下所示扩展相应的样本GetPreSignedUrlRequest.WithContentType会产生一个带有内容类型的工作预签名网址,可以按预期通过 curl 上传(即完全按照您的尝试) :

    // ...
    GetPreSignedUrlRequest request = new GetPreSignedUrlRequest();
    // ...
    request.WithContentType("image/jpg");
    // ...

现在,人们希望以类似的方式扩展语义上相同的样本Upload an Object Using Pre-Signed URL - AWS SDK for .NET,但是(正如您已经发现的那样),没有专门的方法来实现这一点。这可能只是一种缺乏方便的方法,最终可以通过Upload an Object Using Pre-Signed URL - AWS SDK for JavaaddRequestParameter()来实现,例如:

  // ...
  request.setExpiration( new Date( System.currentTimeMillis() + (120 * 60 * 1000) ));
  request.addRequestParameter("content-type", "image/jpg");
  return client.generatePresignedUrl( request ).toString();
  // ...

然而,这两种方法的文档都提出了其他目的,并且它确实不起作用,即它们总是产生相同的签名,无论哪种内容类型设置如此(如果有的话)。

进一步调试SDK显示,两者都提供了一个语义相似的核心方法来根据上面引用的伪语法来计算查询字符串身份验证,请参阅setResponseHeaders() for .NET和Java的buildSigningString()

但是Java版本中的相应代码将所有有趣的标题添加到列表中,然后对它们进行排序,其中“Interesting”定义为Content-MD5,Content-Type,Date实际上从不执行x-amz - ,因为确实没有方法以某种方式提供这些标题,这些方法仅适用于类makeS3CanonicalString()而不是用于初始化的类DefaultRequest前者,用作依次计算签名的输入,参见受保护的方法GeneratePresignedUrlRequest

有趣/值得注意的是,在.NET与Java中计算查询字符串身份验证的两种方法是从 header parameter 源的几乎相反的组合构成它们的输入在调用堆栈上,这可能暗示了Java错误的原因,但显然很难解读,即内部架构当然会有很大的不同。

初步结论

这有两个角度:

  • AWS SDK for Java肯定缺乏设置内容类型的便捷方法,这种方法可能相对较少,但相应的其他AWS开发工具包中明显的用例也是如此 - 考虑到它在AWS中的广泛使用,这是令人惊讶的相关的后端服务。
  • 无论如何,与.NET版本相比,实现查询字符串请求身份验证的方式似乎有些可疑 - 再次这是令人惊讶的,因为它是一个核心功能,但是,这仍然在S3模型/命名空间内,因此可能只需要上面的相应用例。

总之,解决此问题的唯一合理方法是更新SDK,因此错误报告是有序的 - 显然,人们可以复制/扩展SDK功能以分别考虑这种特殊情况(理想情况下)允许提交createRequest()的拉取请求,但以兼容和可维护的方式实现这一点似乎有点棘手,因此最好由SDK维护者自己完成。

答案 1 :(得分:0)

也遇到了这个问题。我们已经跟踪文件在后端上传的时间,因此我们的工作是在客户端使用Rails应用程序上传文件并调用copy_from后设置内容类型。