在执行POST请求时,不能设置Amazon S3内容类型


Amazon S3 Content Type won't be set when doing a POST request

AccessDenied Invalid according to Policy: Extra input fields: content-type

当我有这个

------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="acl"
public-read
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="awsaccesskeyid"
my-access-key-id
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="bucket"
my-bucket
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="Content-Type"
image/png
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="key"
images/anime_girl.png
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="policy"
my-policy
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="signature"
my-signature
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="success_action_status"
201
------WebKitFormBoundaryZIsnhgiAKpAVIsBT
Content-Disposition: form-data; name="file"; filename="anime_girl.png"
Content-Type: image/png

但是,如果我在下面的代码中完全省略Content Type,则文件被上传,但没有正确的内容类型。

        "acl" => "public-read",
        "awsaccesskeyid" => $s3->getAwsAccessKeyId(),
        "bucket" => $s3->getBucket(),
        "Content-Type" => $inputs['type'],
        "key" => "images/" . $inputs['name'],
        "policy" => $s3->getPolicy(true),
        "signature" => $s3->getSignedPolicy(),
        "success_action_status" => "201"

我也试过将内容类型添加到policy中,我得到这个错误

AccessDenied Invalid according to Policy: Policy Condition failed: ["starts-with", "Content-Type", "image/png"]

我尝试了一些其他的建议,得到这个错误

AccessDenied Invalid according to Policy: Policy Condition failed: ["eq", "Content-Type", "image/png"]

这是我的cors配置

<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>

那么我如何以另一种方式指定内容类型或修复这个问题呢?

编辑

当我省略任何类型的内容类型(我没有指定任何后端),我得到一个请求有效负载像Ben的

------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="acl"
public-read
------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="awsaccesskeyid"
xxxx
------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="bucket"
xxxxx
------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="key"
images/Elf-Anime-Warrior-Wallpaper.jpg
------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="policy"
xxxx
------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="signature"
0BbpI0AP0wL3S1cBfo9n9tOp+N8=
------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="success_action_status"
201
------WebKitFormBoundaryUjxIo0gCBYg7AqJt
Content-Disposition: form-data; name="file"; filename="Elf-Anime-Warrior-Wallpaper.jpg"
Content-Type: image/jpeg

------WebKitFormBoundaryUjxIo0gCBYg7AqJt--

问题是,在桶中,文件的内容类型为binary/octet-stream,所以图像不是在浏览器中查看,而是下载(这不是我想要的)。所以我不知道为什么亚马逊没有设置内容类型适当当我的请求有效载荷看起来像上面。

根据Amazon S3文档,您确实可以将Content-Type作为表单字段传递,但您需要在策略中允许这一点。

所以你需要这样写:

政策:

{
  "expiration": "2007-12-01T12:00:00.000Z",
  "conditions": [
    {"acl": "public-read" },
    {"bucket": "johnsmith" },
    ["starts-with", "$key", "user/eric/"],
    ["starts-with", "$Content-Type", ""],
    ...
  ]
}

这里的相关行是["starts-with", "$Content-Type", ""]。文档指出,"match any"类型的条件应该是带有空值的"begin -with"。如果你想限制内容类型,你应该使用["starts-with", "$Content-Type", "image/"],例如。

形式:

(示例取自S3文档,请相应地更改值)。

<form action="http://examplebucket.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
  <input type="hidden" name="key" value="user/user1/${filename}">
  <input type="hidden" name="acl" value="public-read">
  <input type="hidden" name="success_action_redirect" value="http://examplebucket.s3.amazonaws.com/successful_upload.html">
  <input type="hidden" name="Content-Type" value="binary/octet-stream">
  <input type="hidden" name="Policy" value='<Base64-encoded policy string>'>
  <input type="hidden" name="x-amz-meta-uuid" value="14365123651274">
  <input type="hidden" name="X-Amz-Signature" value="<signature-value>">
  <input type="hidden" name="X-Amz-Credential" value="AKIAIOSFODNN7EXAMPLE/20130806/us-east-1/s3/aws4_request">
  <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256">
  <input type="hidden" name="X-Amz-Date" value="20130806T000000Z">
  Tags for File: 
  <input type="input" name="x-amz-meta-tag" value=""><br>
  File: 
  <input type="file" name="file"><br>
  <input type="submit" name="submit" value="Upload to Amazon S3">
</form>

正如您在第一个有效负载中所做的那样,您为表单设置了一个"Content-Type"字段。这与更新后的策略一起,应该可以工作。

作为旁注,如果您事先知道要上传的文件的类型,则应该将策略和表单字段设置为该类型。但是如果你上传的是不同类型的文件,你可以在策略中设置Content-Type条件为"match any",你可以编写一个简单的javascript拦截表单的submit事件来动态地改变Content-Type字段(这里是在jQuery的帮助下编写的):

$("#s3-form").on("submit", function() {
  var contentTypeField = $("input[name='Content-Type']", this),
      fileField = $("input[type='file']", this),
      selectedFiles = fileField.get(0).files;
  if (files.length > 0) {
    // we're uploading a single file, so get the type for the first selected file
    contentTypeField.val(selectedFiles[0].type);
  }
});

下面是我将文件上传到S3时的工作请求负载示例:

------WebKitFormBoundary9Vtjs2zgAF5jJgHC
Content-Disposition: form-data; name="key"
uploads/tmp/dbdfc12f-665e-4389-a739-f345e7acea48/svg_3927.zip
------WebKitFormBoundary9Vtjs2zgAF5jJgHC
Content-Disposition: form-data; name="AWSAccessKeyId"
xxx
------WebKitFormBoundary9Vtjs2zgAF5jJgHC
Content-Disposition: form-data; name="acl"
public-read
------WebKitFormBoundary9Vtjs2zgAF5jJgHC
Content-Disposition: form-data; name="policy"
xxx
------WebKitFormBoundary9Vtjs2zgAF5jJgHC
Content-Disposition: form-data; name="signature"
xxx=
------WebKitFormBoundary9Vtjs2zgAF5jJgHC
Content-Disposition: form-data; name="success_action_status"
201
------WebKitFormBoundary9Vtjs2zgAF5jJgHC
Content-Disposition: form-data; name="file"; filename="svg_3927.zip"
Content-Type: application/zip

正如您所看到的,Content-type只在末尾与文件数据一起设置一次。

更新:

下面是我的开发桶的CORS:

    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
    <AllowedHeader>Content-*</AllowedHeader>
    <AllowedHeader>Host</AllowedHeader>
    <AllowedHeader>*</AllowedHeader>

注意<AllowedHeader>Content-*</AllowedHeader>,它可能解决您的问题。

尝试在'prams'级数组中偏移ContentType .

"acl" => "public-read",
"awsaccesskeyid" => $s3->getAwsAccessKeyId(),
"bucket" => $s3->getBucket(),
"key" => "images/" . $inputs['name'],
"policy" => $s3->getPolicy(true),
"signature" => $s3->getSignedPolicy(),
"success_action_status" => "201",
// indent "ContentType" within a "params" array
"params" => array(
    "ContentType" => $inputs['type']
)