AWS S3 – Ruby – Presigned POST: Content-Type policy failing

问题内容:

I am trying to specify the content type of a direct to S3 upload a getting the following 403 error:

Invalid according to Policy: Policy Condition failed: [“starts-with”,
“$Content-Type”, “”]

I have put in everything I could find from other posts on here to no avail. My code:

config/initializers/aws.rb

Aws.config.update({
  region: 'us-east-1',
  credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']),
})

S3_BUCKET_NAME = Aws::S3::Resource.new.bucket(ENV['S3_BUCKET_NAME'])

doc_uploads_controller.rb

before_action :set_s3_direct_post
private
def set_s3_direct_post
  @s3_direct_post = S3_BUCKET_NAME.presigned_post(key: "my_path/${filename}", success_action_status: '201', acl: 'authenticated-read', server_side_encryption: 'AES256', content_type_starts_with: '')
end

new.html.erb

<%= form_for(@doc_upload, url: new_doc_upload_path, html: { class: "directUpload" }, data: { 'form-data' => (@s3_direct_post.fields), 'url' => @s3_direct_post.url, 'host' => URI.parse(@s3_direct_post.url).host }) do |f| %>
  <%= render 'errors', f: f %>
  <%= hidden_field_tag  "Content-Type", "" %>
  <div class="row">
    <span class="btn btn-success fileinput-button">
      <span>Select File</span>
      <!-- The file input field used as target for the file upload widget -->
      <input id="doc_upload_file_url" type="file" name="doc_upload[file_url]">
    </span>
    <!-- The container for the uploaded files -->
    <div id="files" class="files"></div>
    <div id="progress" class="progress">
      <div class="progress-bar progress-bar-success"></div>
    </div>
  </div>
  <div class="row">
    <p><%= f.button "Save", id: 'doc-upload-submit', disabled: true, data: {disable_with: "<i class='fa fa-spinner fa-spin'></i> Saving..."}, class: "btn btn-form btn-warning" %></p>
    <p><%= link_to 'Cancel', doc_uploads_path, id: 'cancel-link' %></p>
  </div>
<% end %>

doc_uploads.js.coffee

$(document).on 'turbolinks:load', ->
  $ ->
    'use strict'
    url = $('.directUpload').find("input:file").parents('form:first').data('url')
    formdata = $('.directUpload').find("input:file").parents('form:first').data('form-data')
    uploadButton = $('<button/>')
      .addClass('btn btn-default btn-upload')
      .prop('type', 'button')
      .prop('disabled', true)
      .text('Processing...')
      .on('click', ->
        $('#cancel-link').hide()
        $this = $(this)
        data = $this.data()
        $this.off('click').text('Abort').on('click', ->
          $this.remove()
          data.abort()
          $('#cancel-link').show()
        )
        data.submit().always( ->
          $this.remove()
        )
      )
    $('#doc_upload_file_url').fileupload({
      url: url,
      type: 'POST',
      autoUpload: false,
      formData: formdata,
      paramName: 'file',
      dataType: 'XML',
      replaceFileInput: false,
      maxNumberOfFiles: 1,
      acceptFileTypes: /(\.|\/)(jpe?g|jpg|png|pdf|doc|docx|xls|xlsx)$/i,
      maxFileSize: 104857600,
      uploadTemplateId: null,
      downloadTemplateId: null,
      dropZone: null
      }).on('fileuploadadd', (e, data) ->
        $('#filename').remove()
        data.context = $('<div/>').prop('id', 'filename').appendTo('#files')
        $.each(data.files, (index, file) ->
          node = $('<p/>').append($('<span/>').text(file.name))
          node.appendTo(data.context)
          if (!index)
            $('#filename').append(uploadButton.clone(true).data(data))
        )
      ).on('fileuploadprocessalways', (e, data) ->
        index = data.index
        file = data.files[index]
        $('#Content-Type').attr('value',file.type)
        node = $(data.context.children()[index])
        if (file.error)
          node.append('<br>').append($('<span class="text-danger"/>').text(file.error))
        if (index + 1 == data.files.length)
          data.context.find('button').text('Upload').prop('disabled', !!data.files.error)
      ).on('fileuploadprogressall', (e, data) ->
        progress = parseInt(data.loaded / data.total * 100, 10)
        $('#progress .progress-bar').css('width',progress + '%')
      ).on('fileuploaddone', (e, data) ->
        form = $('.directUpload').find("input:file")
        key = $(data.jqXHR.responseXML).find("Key").text()
        input = $("<input />", { type:'hidden', name: form.attr('name'), value: key })
        form.append(input)
        $('#progress .progress-bar').css('background', 'green').text("Upload Complete")
        $('#doc-upload-submit').prop('disabled', false)
        $('#doc-upload-submit').trigger('click')
      ,
      ).on('fileuploadfail', (e, data) ->
        message = $(data.jqXHR.responseXML).find("Message").text()
        $.each(data.files, (index) ->
          error = $('<span class="text-danger"/>').text('File upload failed.')
          $(data.context.children()[index]).append('<br>').append(error)
        )
      ).prop('disabled', !$.support.fileInput).parent().addClass($.support.fileInput ? undefined : 'disabled')

When I remove the content_type_starts_with: setting from the presigned post everything works fine minus the setting of the content type in S3. Am I not setting Content-Type correctly in the form prior to submitting it?
Please help!

Update: I tried replacing the hidden field update with the following, but the 403 response persists.

formdata["Content-Type"] = file.type
console.log(formdata)
data.formData = formdata
console.log(data.formData)

The console shows the resulting data.formData as:

{key: "uploads/1/${filename}", success_action_status: "201", acl: "authenticated-read", x-amz-server-side-encryption: "AES256", policy: "eyJleHBpcmF0aW9uIjoiMjAxNy0xMi0xMVQwNDo1Mzo1NFoiLC…seyJ4LWFtei1kYXRlIjoiMjAxNzEyMTFUMDM1MzU0WiJ9XX0=", …}
  Content-Type: "application/pdf"
  acl: "authenticated-read"
  key: "my_path/${filename}"
  policy: "eyJleHBpcmF0aW9uIjoiMjAxNy0xMi0xMVQwNDo1Mzo1NFoiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJlZy1zdGFnaW5nLXVwbG9hZHMifSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVwbG9hZHMvMS8iXSx7InN1Y2Nlc3NfYWN0aW9uX3N0YXR1cyI6IjIwMSJ9LHsiYWNsIjoiYXV0aGVudGljYXRlZC1yZWFkIn0seyJ4LWFtei1zZXJ2ZXItc2lkZS1lbmNyeXB0aW9uIjoiQUVTMjU2In0sWyJzdGFydHMtd2l0aCIsIiRDb250ZW50LVR5cGUiLCIiXSx7IngtYW16LWNyZWRlbnRpYWwiOiJBS0lBSURKNE1LRjdMTlA3RDZLUS8yMDE3MTIxMS91cy1lYXN0LTEvczMvYXdzNF9yZXF1ZXN0In0seyJ4LWFtei1hbGdvcml0aG0iOiJBV1M0LUhNQUMtU0hBMjU2In0seyJ4LWFtei1kYXRlIjoiMjAxNzEyMTFUMDM1MzU0WiJ9XX0="
  success_action_status: "201"
  x-amz-algorithm: "AWS4-HMAC-SHA256"
  x-amz-credential: "[redacted]"
  x-amz-date: "20171211T035354Z"
  x-amz-server-side-encryption: "AES256"
  x-amz-signature:"[redacted]"
  __proto__:Object

问题评论:

答案:

答案1:

You need to set Content-Type on the formData, instead of in the hidden field.

Instead of:

$('#Content-Type').attr('value',file.type)

try:

formdata["Content-Type"] = file.type
data.formData = formdata

This worked for me, but I was only uploading one file at a time. Since you’re uploading multiple, if they have different content types this might not work, but worth a try.

答案评论:

    
I tried this to no avail, the 403 error remains. See updated post.
– CChandler81
12 hours ago
    
Forgot to mention that I’m only trying this with one file at the moment. Thanks.
– CChandler81
12 hours ago
    
Can you try setting the content type in ‘fileuploadadd’

原文地址:

https://stackoverflow.com/questions/47739964/aws-s3-ruby-presigned-post-content-type-policy-failing

添加评论

友情链接:蝴蝶教程