【问题标题】:S3 PUT doesn't work with pre-signed URL in javascriptS3 PUT 不适用于 javascript 中的预签名 URL
【发布时间】:2015-03-05 06:50:40
【问题描述】:

我在 Java 中为 HTTP PUT 方法生成了一个预签名的 S3 URL。

网址稍作修改:

https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIAIQ6H5Z7BG6KZ2ZUA&Expires=1425617244&Signature=GWcRM5ZIrAKnMJBsxyfm%2F9fyuBk%3D

我知道这是一个有效的预签名 URL,因为我可以将它与 curl 一起使用来上传文件

curl -v --upload-file somefile.txt "https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIAIQ6H5Z7BG6KZ2ZUA&Expires=1425617244&Signature=GWcRM5ZIrAKnMJBsxyfm%2F9fyuBk%3D"

当我尝试使用以下 Javascript 将文件上传到同一 URL 时:

function ajaxUsingPresignedUrlPut() {
            $.ajax({
                url: presignedURLPUT,
                type: 'PUT',
                data: 'blah blah blah',
                success: function () {
                    console.log('Uploaded data successfully.');
                }
            }).done(function (data) {
                console.log("DONE : " + data);
            }).fail(function (e, r) {
                console.log("FAIL");
            });
        }

我得到一个 403 状态和 responseText

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message><AWSAccessKeyId>BKIAIQ6H5Z7BG6KZ2ZUA</AWSAccessKeyId><StringToSign>PUT

application/x-www-form-urlencoded; charset=UTF-8
1425617244
/somebucket/pre-signed-url-key-2</StringToSign><SignatureProvided>GWcRM5ZIrAKnMJAsxyfm/9fyuAk=</SignatureProvided><StringToSignBytes>50 55 54 0a 0a 61 70 70 6c 69 63 61 74 69 6f 6e 2f 78 2d 77 77 77 2d 66 6f 72 6d 2d 75 72 6c 65 6e 63 6f 64 65 64 3b 20 63 68 61 72 73 65 74 3d 55 54 46 2d 38 0a 31 34 32 35 36 31 37 32 34 34 0a 2f 6e 6b 6f 6e 64 72 61 74 5f 62 75 63 6b 65 74 2f 70 72 65 2d 73 69 67 6e 65 64 2d 75 72 6c 2d 6b 65 79 2d 32</StringToSignBytes><RequestId>3C8298CAC404C6F5</RequestId><HostId>uCkzA//CdCLi4SINifkIe0WH6GOlCJBgFlN8ghx8NnULEe+QVslsdoUsJc4AUdA8</HostId></Error>

我在 S3 存储桶上配置了以下 CORS 策略:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>HEAD</AllowedMethod>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <ExposeHeader>ETag</ExposeHeader>
        <ExposeHeader>Content-Length</ExposeHeader>
        <ExposeHeader>Content-Type</ExposeHeader>
        <ExposeHeader>Connection</ExposeHeader>
        <ExposeHeader>Date</ExposeHeader>
        <ExposeHeader>Server</ExposeHeader>
        <ExposeHeader>x-amz-delete-marker</ExposeHeader>
        <ExposeHeader>x-amz-id-2</ExposeHeader>
        <ExposeHeader>x-amz-request-id</ExposeHeader>
        <ExposeHeader>x-amz-version-id</ExposeHeader>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

同样的 javascript 代码适用于预签名的 GET URL: https://s3.amazonaws.com/somebucket/pre-signed-url-key-2?AWSAccessKeyId=BKIBIQ6H5Z7LG6KZ2ZUB&amp;Expires=1425616588&amp;Signature=qMPpuzMwxonYPDQETdLafEQGqMU%3D

function ajaxUsingPresignedUrlGET() {
            $.ajax({
                url: presignedURLGET,
                type: 'GET',
                success: function () {
                    console.log('Uploaded data successfully.');
                }
            }).done(function (data) {
                console.log("DONE : " + data);
            }).fail(function (e, r) {
                console.log("FAIL");
            });
        }

我现在查看 URL 的唯一猜测是 PUT url 包含 %2F 并查看代表 /http://www.w3schools.com/tags/ref_urlencode.asp 可能会导致浏览器出现问题?或者,也许我完全不在这儿了,还有其他一些我遗漏的问题。


Edit #1 添加用于生成预签名 URL 的 java 代码

public S3GeneratePresignedUrlDemo(HttpMethod httpMethod) {
    this.httpMethod = httpMethod;
    System.out.println("HTTP METHOD : " + this.httpMethod);
}

@Override
public void goImpl() throws Exception {        
    GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, KEY);

    request.setMethod(httpMethod);

    request.setExpiration(createDateNHoursFromNow(24));

    URL url = s3Client.generatePresignedUrl(request);

    System.out.println(url);
}

private static Date createDateNHoursFromNow(int hours){
    Date date = new Date();
    long millis = date.getTime();
    final int millisInSecond = 1000;
    final int secondsInMinute = 60;
    final int minutesInHour = 60;
    millis += millisInSecond * secondsInMinute * minutesInHour * hours;

    date.setTime(millis);
    return date;
}`

【问题讨论】:

  • 我相信该方法(例如 GET 或 PUT)用于生成预签名 URL,因此这可能解释了您所看到的问题。
  • 我在生成 URL 时指定了 HTTP 方法。如果我尝试使用curl 上传带有使用GET 方法生成的URL 的文件,我会收到错误,而curl 确实可以使用我使用PUT 生成的URL(编辑#1 显示我用来生成预签名 URL 的 java 代码)
  • 你已经接近了,确保签名匹配并且没有额外的空格或引号偷偷进入。

标签: javascript url amazon-web-services amazon-s3 urlencode


【解决方案1】:

添加

headers: {'Content-Type': 'text/plain;charset=UTF-8'},

到 AJAX

function ajaxUsingPresignedUrlPut() {
            $.ajax({
                url: presignedURLPUT,
                type: 'PUT',
                data: 'blah blah blah',
                headers: {'Content-Type': 'text/plain;charset=UTF-8'},
                success: function () {
                    console.log('Uploaded data successfully.');
                }
            }).done(function (data) {
                console.log("DONE : " + data);
            }).fail(function (arg1, arg2) {
                console.log("FAIL");        
            });
        }

在生成 URL 时设置内容类型解决了这个问题

    GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, KEY);        
    request.setContentType("text/plain;charset=UTF-8");
    //...

【讨论】:

  • 当它可用时,我会给你 100 分,因为我在这个问题上花了太长时间,最终解决了它。我也遇到了与 curl 完全相同的情况。
  • 其他内容类型也可以,只要签名和实际的PUT请求相同。
  • 非常感谢!!花了这么多时间试图弄清楚这一点
【解决方案2】:

实际上有一个更好的方法来设置内容类型,这似乎是这里的问题:

$.ajax({
    url : s3presignedUrl,
    type : "PUT",
    data : file,
    dataType : "text",
    cache : false,
    contentType : file.type,
    processData : false
})

对您来说重要的部分是contentType : file.type,其中 file.type 来自&lt;input type=file&gt; onchange 事件(它实际上揭示了this.files,然后可以对其进行迭代......)

您可以在此处从我的 php+js 解决方案中获取完整的客户端文件:https://github.com/JoernBerkefeld/s3SignedUpload

【讨论】:

    【解决方案3】:

    如果您不想像 Nickolay Kondratyev 建议的那样限制内容类型,您可以像这样设置 Content-Type:

    headers: {'Content-Type': ' '},
    

    注意,有一个带有空格字符的字符串。非 null、非未定义、空字符串或任何其他虚假值。它有效。


    更新:这不再起作用,S3 现在检测 mime 类型并添加它

    【讨论】:

      猜你喜欢
      • 2017-11-26
      • 1970-01-01
      • 2012-04-23
      • 2021-03-17
      • 1970-01-01
      • 1970-01-01
      • 2022-07-16
      • 1970-01-01
      • 2016-03-18
      相关资源
      最近更新 更多