AWS S3 - Ruby - 预先指定的POST:内容类型策略失败

时间:2017-12-10 14:48:54

标签: javascript ruby-on-rails ruby amazon-s3

我正在尝试指定直接上传到S3的内容类型获得以下403错误:

  

根据政策无效:政策条件失败:[" start-with",   " $ Content-Type",""]

我已将其他帖子中找到的所有内容都放在此处无济于事。我的代码:

配置/初始化/ 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')

当我从预先签名的帖子中删除content_type_starts_with:设置时,一切正常,减去S3中内容类型的设置。在提交之前,我没有在表单中正确设置Content-Type吗?  请帮忙!

更新:我尝试用以下内容替换隐藏字段更新,但403响应仍然存在。

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

控制台会将结果data.formData显示为:

{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 个答案:

答案 0 :(得分:0)

您需要在formData上设置Content-Type,而不是在隐藏字段中设置。

而不是:

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

尝试:

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

这对我有用,但我一次只上传一个文件。由于您要上传多个内容,如果它们有不同的内容类型,这可能无效,但值得一试。

更新:这里的代码对我有用,但请记住它直接自动上传到S3,所以它可能与你正在做的不同,但希望它有所帮助。

# @s3ResumeFields came from @s3_direct_post.fields
formData = @s3ResumeFields

resumeInput.fileupload
    fileInput: resumeInput,
    url: @s3ResumeUrl, # this came from @s3_direct_post.url
    type: 'POST',
    autoUpload: true,
    formData: formData,
    paramName: 'file',
    dataType: 'XML',
    replaceFileInput: false
    add: (e, data) ->
        formData["Content-Type"] = data.files[0].type
        data.formData = formData
        data.submit()
    progressall: (e, data) ->
        # unrelated UI updates to show progress
    start: (e) ->
        # unrelated UI updates to show progress
    done: (e, data) ->
        # unrelated UI updates to notify that the upload completed

    fail: (e, data) ->
        # unrelated UI updates to show upload failed