通过预签名URL将文件上传到AWS S3时Javascript不起作用

时间:2019-09-14 03:29:42

标签: javascript python amazon-web-services amazon-s3

我生成了一个预签名URL,用于通过Python支持的API将文件上传到AWS S3。用户在浏览器中选择文件后,此部分将接收文件名信息(请参见下文),并返回带有基本URL和字段的JSON有效负载(请参见下文)。

import logging
import boto3
from botocore.exceptions import ClientError


def create_presigned_post(bucket_name, object_name,
                          fields=None, conditions=None, expiration=3600):

    # Generate a presigned S3 POST URL
    s3_client = boto3.client('s3')
    try:
        response = s3_client.generate_presigned_post(bucket_name,
                                                     object_name,
                                                     Fields=fields,
                                                     Conditions=conditions,
                                                     ExpiresIn=expiration)
    except ClientError as e:
        logging.error(e)
        return None

    # The response contains the presigned URL and required fields
    return response

这是我从此函数获得的JSON响应。 (我更改/缩写了一些值,但您明白了。)

{
  "url": "https://s3.us-east-2.amazonaws.com/my_bucket_name",
  "fields": {
    "key": "some.txt",
    "AWSAccessKeyId": "ASI...",
    "x-amz-security-token": "Ag9o...",
    "policy": "eyJ...=",
    "signature": "jn...="
  }
}

这是我用来上传文件的HTML表单。我有一些原始的Javascript可以跟踪对表单的更改,并在更改后(例如,文件选择)更新表单的URL_VALUE和每个表单项的VALUE

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    <!-- Copy the 'url' value returned by S3Client.generate_presigned_post() -->
    <form action="URL_VALUE" method="post" enctype="multipart/form-data">
      <!-- Copy the 'fields' key:values returned by S3Client.generate_presigned_post() -->
      <input type="hidden" name="key" value="VALUE" />
      <input type="hidden" name="AWSAccessKeyId" value="VALUE" />
      <input type="hidden" name="policy" value="VALUE" />
      <input type="hidden" name="signature" value="VALUE" />
    File:
      <input type="file"   name="file" /> <br />
      <input type="submit" name="submit" value="Upload to Amazon S3" />
    </form>
  </body>
</html>

这种HTML表单本身可以正常工作,但是我尝试添加一些Javascript(包括香草和JQuery),因此我可以跟踪文件进度并禁用表单输入,直到上传完成。

我无法使用Javascript!

我尝试了很多示例(同样是香草JS和JQuery)。

最近有没有人实施过这个并且可以提供帮助?

3 个答案:

答案 0 :(得分:1)

我相信您必须传递

之类的AWS机密
"key": "some.txt",
    "AWSAccessKeyId": "ASI...",
    "x-amz-security-token": "Ag9o...",
    "policy": "eyJ...=",
    "signature": "jn...="

作为标题。

您是否正在使用fetch库? 能否将JS代码发布到?

答案 1 :(得分:0)

好的,找到了一个ridiculously simple香草JS示例,可以正常工作!


$(document).ready(function () {
    var PRESIGNED_URL = ""

    $('input[type="file"]').change(function (e) {
        var fileName = e.target.files[0].name;

        var settings = {
            "async": true,
            "crossDomain": true,
            "url": "https://my_api.com",
            "method": "POST",
            "headers": {
                "Content-Type": "application/x-www-form-urlencoded",
            },
            "data": {
                "filename": fileName
            }
        }

        $.ajax(settings).done(function (response) {
            $("#filename").html(fileName)
            PRESIGNED_URL = response["url"]

            $("#form").attr("action", response["url"])
            $("#key").val(response["fields"]["key"])
            $("#AWSAccessKeyId").val(response["fields"]["AWSAccessKeyId"])
            $("#policy").val(response["fields"]["policy"])
            $("#signature").val(response["fields"]["signature"])
            $("#x-amz-security-token").val(response["fields"]["x-amz-security-token"])

            return
        });
    });

    $("#button").on("click", function (e) {
        var form = document.getElementById('form');
        var formData = new FormData(form);

        var xhr = new XMLHttpRequest();
        // Add any event handlers here...

        xhr.upload.addEventListener('progress', function(e) {
            var percent_complete = (e.loaded / e.total)*100;

            // Percentage of upload completed
            console.log(percent_complete);
        });
        xhr.onloadstart = function (e) {
            console.log("start")
        }
        xhr.onloadend = function (e) {
            console.log("end")
        }

        xhr.open('POST', PRESIGNED_URL, true);
        xhr.send(formData);

        $("#filename").html("")
    })
});



有很多相似的变体,但是效果很好。

(我相信对许多人来说似乎很明显,但我只是根据需要进行前端开发...)

答案 2 :(得分:0)

比您发布的内容还要简单。

fetch(yourSignedUrl, {
  method: 'PUT',
  body: file,
  headers: {
    'Content-Type': file.type
  }
}).then((res) => {
  if (!res.ok) {
    throw new Error(res.statusText);
  }
  return res.headers.get('ETag');
});