amazon-web-servicesamazon-s3

AWS S3 form-based POST file upload ignores Content-Type


When using a form POST to upload a file to an S3 bucket, S3 ignores the Content-Type/MIME type of the file.

The file is uploaded successfully. The file form data field contains a Blob with a Content-Type of video/x-matroska;codecs=avc1,pcm. The key form data field sets the key of where that file goes in the bucket. But, when I HEAD or GET that object later, its Content-Type is just binary/octet-stream.

Just for kicks, I added a form field named Content-Type and duplicated the type of the Blob, but that didn't work. I also tried ContentType and content_type, and those didn't work either.

Is it just not possible to set the type via a form upload?

Here's some of the raw request data so you can see that the data is indeed being sent to S3.

------WebKitFormBoundaryJJ9VA3o1bbQ3DIPJ
Content-Disposition: form-data; name="key"

woot/woot/wootwootwoot
------WebKitFormBoundaryJJ9VA3o1bbQ3DIPJ
Content-Disposition: form-data; name="file"; filename="woot/woot/wootwootwoot"
Content-Type: video/x-matroska;codecs=avc1,pcm

Eߣ£BB÷BòBóBmatroskaBBSgÿÿÿÿÿÿÿI©f*×±B@MChromeWAChromeT®kà®±×sÅoù¾¤=ÆA_PCM/FLOAT/IEEEáµG,Dbd ®«×sź<ø
V_MPEG4/ISO/AVCà°º8
------WebKitFormBoundaryJJ9VA3o1bbQ3DIPJ
Content-Disposition: form-data; name="Content-Type"

video/x-matroska;codecs=avc1,pcm
------WebKitFormBoundaryJJ9VA3o1bbQ3DIPJ
Content-Disposition: form-data; name="ContentType"

video/x-matroska;codecs=avc1,pcm
------WebKitFormBoundaryJJ9VA3o1bbQ3DIPJ
Content-Disposition: form-data; name="content_type"

video/x-matroska;codecs=avc1,pcm
------WebKitFormBoundaryJJ9VA3o1bbQ3DIPJ--

Solution

  • This is due to Content-Type not being set in your Form Data. To do so you need to change your policy to allow Content-Type you wish to pass. In your case a video video/x-matroska. For that you need to set the policy first. After that specify it on the HTML form data as well.

    Refer to the code below and the sample policy:

    {
      "expiration": "2024-12-30T12:00:00.000Z",
      "conditions": [
        {"bucket": "your-bucket-name"},
        ["starts-with", "$key", "uploads/"],
        {"acl": "public-read"},
        {"success_action_status": "201"},
        ["starts-with", "$Content-Type", ""],
        ["content-length-range", 0, 10485760]  // Adjust the range as per your need
      ]
    }
    

    Make sure to provide the content type in your template code in the HTML like below:

    <form action="https://your-bucket-name.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
      <input type="hidden" name="key" value="uploads/${filename}">
      <input type="hidden" name="acl" value="public-read">
      <input type="hidden" name="Content-Type" value="video/x-matroska;codecs=avc1,pcm"> <!-- Add content type like this -->
      <input type="hidden" name="X-Amz-Credential" value="${credentials}">
      <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256">
      <input type="hidden" name="X-Amz-Date" value="${timestamp}">
      <input type="hidden" name="Policy" value="${policy}">
      <input type="hidden" name="X-Amz-Signature" value="${signature}">
      <input type="file" name="file">
      <input type="submit" value="Upload">
    </form>

    Note: It is very important that the Content-Type form field come before the file field.

    Refer the AWS S3 upload official documentation here for more reference.