使用预先签名的URL通过curl上传到s3(获得403)

我正在使用curl调用Java ReST API来检索URL。 然后,Java使用我的S3凭据生成用于S3上载的预签名URL,并在ReST回复中返回该URL。 Curl获取URL并使用它上传到S3,但S3返回403“我们计算的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法。”

这是我用来生成预签名URL的代码:

public class S3Util { static final AmazonS3 s3 = new AmazonS3Client( new AWSCredentials() { @Override public String getAWSAccessKeyId() { return "XXXXXXX"; } @Override public String getAWSSecretKey() { return "XXXXXXXXXXXXXX"; } }); static final String BUCKET = "XXXXXXXXXXXXXXXXXXXXXXXXXXX"; static public URL getMediaChunkURL( MediaChunk mc, HttpMethod method ) { String key = ... //way in the future (for testing)... Date expiration = new Date( System.currentTimeMillis() + CalendarUtil.ONE_MINUTE_IN_MILLISECONDS*60*1000 ); GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, method); req.setExpiration(expiration); req.addRequestParameter("Content-Type", "application/octet-stream"); //this gets passed to the end user: return s3.generatePresignedUrl(req); } } 

在curl中,从bash运行,我执行:

 echo Will try to upload chunk to ${location} curl -i -X POST \ -F 'Content-Type=application/octet-stream' \ -F "file=@${fileName}" \ ${location} || (echo upload chunk failed. ; exit 1 ) 

除此之外,我尝试了PUT,我尝试过“Content-type”(小写T)。 我意识到我错过了一些显而易见的东西(或者某些东西),但在阅读了相应的文档,谷歌搜索并查看了许多类似的问题后,我不确定那是什么。 我看到很多关于必需标头的提示,但我认为重新设置的URL应该可以消除这些需求。 也许不吧?

TIA!

更新:

为了清楚起见,我测试了下载,这很好。

Java看起来像:

 GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(BUCKET, key, HttpMethod.GET); req.setExpiration(expiration); 

和curl简单地说:

 curl -i ${location} 

我已经能够通过C#生成预先签名的URL,然后按预期通过curl上传。 鉴于我的测试,我怀疑你确实没有正确使用curl – 我已经能够上传一个这样的文件:

 curl -v --upload-file ${fileName} ${location} 

参数-v转储请求和响应头(以及SSL握手)以进行调试和说明:

 > PUT [...] HTTP/1.1 > User-Agent: curl/7.21.0 [...] > Host: [...] > Accept: */* > Content-Length: 12 > Expect: 100-continue 

请注意, – --upload-file (或-T )可以按预期方式促进PUT ,但会根据需要添加更多标头,从而产生适当的响应:

 < HTTP/1.1 100 Continue < HTTP/1.1 200 OK < x-amz-id-2: [...] < x-amz-request-id: [...] < Date: Tue, 31 Jan 2012 18:34:56 GMT < ETag: "253801c0d260f076b0d5db5b62c54824" < Content-Length: 0 < Server: AmazonS3 

当使用curl执行此操作时,您需要将url放在单引号中,否则一半的查询字符串会被切断(带有键/签名的部分)。

生成URL的方法:

 private static URL generateRUL(String objectKey, String ACCESS_KEY, String SECRET_KEY, String BUCKET_NAME) { AmazonS3 s3Client = new AmazonS3Client(new BasicAWSCredentials(ACCESS_KEY, SECRET_KEY)); URL url = null; try { GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(BUCKET_NAME, objectKey); request.setMethod(com.amazonaws.HttpMethod.PUT); request.setExpiration(new Date( System.currentTimeMillis() + (60 * 60 * 1000))); // Very important ! It won't work without adding this! // And request.addRequestParameter("Content-Type", "application/octet-stream") won't work neither request.setContentType("application/octet-stream"); url = s3Client.generatePresignedUrl(request ); } catch (AmazonServiceException exception) { } catch (AmazonClientException ace) { } return url; } 

上传文件的方式:

 public int upload(byte[] fileBytes, URL url) { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoOutput(true); connection.setRequestMethod("PUT"); connection.setRequestProperty("Content-Type", "application/octet-stream"); // Very important ! It won't work without adding this! OutputStream output = connection.getOutputStream(); InputStream input = new ByteArrayInputStream(fileBytes); byte[] buffer = new byte[4096]; int length; while ((length = input.read(buffer)) > 0) { output.write(buffer, 0, length); } output.flush(); return connection.getResponseCode(); } 

尽管GeneratePresignedUrlRequest接受了一个http方法参数(并且有一个setMethod函数),但除了GET之外,它似乎无法使用。

http://wiki.nercomp.org/wiki/images/0/05/AmazonWebServices.pdf声明“签署请求并将其提供给第三方执行的做法仅适用于简单的对象GET请求。” 也许设置另一种方法可以用于某些东西,但显然不是这个。

所以,相反,我必须遵循这里的说明:

http://aws.amazon.com/articles/1434?_encoding=UTF8&jiveRedirect=1

这更复杂,因为客户端需要发布完整的表单,而不仅仅是使用URL,并且还意味着所有post信息必须单独传达给客户端,但它似乎确实有效。