使用jQuery文件上载在S3中设置文件内容类型

时间:2013-08-02 13:29:14

标签: ruby-on-rails amazon-s3 jquery-file-upload

我需要通过客户端jQuery上传表单向amazon提供内容类型的帮助。我需要添加内容类型,因为我上传的音频文件不会在jPlayer中播放ie10除非内容类型设置正确。我使用了pjambet的博客文章 - http://pjambet.github.io/blog/direct-upload-to-s3/来启动和运行(优秀的文章顺便说一句)。看来,字段的顺序非常重要。我一直试图插入一个隐藏的输入标签,或者说是相关的内容类型(我认为是音频/ mpeg3),或者是空白,以便我的上传脚本填充。没运气。添加额外字段时上传会挂起。

直接上传-form.html.erb

<form accept-charset="UTF-8" action="http://my_bucket.s3.amazonaws.com" class="direct-upload" enctype="multipart/form-data" method="post"><div style="margin:0;padding:0;display:inline"></div>

    <%= hidden_field_tag :key, "${filename}" %>
    <%= hidden_field_tag "AWSAccessKeyId", ENV['AWS_ACCESS_KEY_ID'] %>
    <%= hidden_field_tag :acl, 'public-read' %>
    <%= hidden_field_tag :policy %>
    <%= hidden_field_tag :signature %>
    <%= hidden_field_tag :success_action_status, "201" %>
    <%= file_field_tag :file %>

    <div class="row-fluid">
        <div class="progress hide span8">
            <div class="bar"></div>
        </div>
    </div>

</form>

音频upload.js

$(function() {
  $('input[type="submit"]').attr("disabled","true");
  $('input[type="submit"]').val("Please upload audio first");

  if($('#demo_audio').val() != ''){
    var filename = $('#demo_audio').val().split('/').pop().split('%2F').pop();
    $('#file_status').removeClass('label-info').addClass('label-success').html(filename + ' upload complete');
  }

  $('.direct-upload').each(function() {    
    var form = $(this)

    $(this).fileupload({
      url: form.attr('action'),
      type: 'POST',
      autoUpload: true,
      dataType: 'xml', // This is really important as s3 gives us back the url of the file in a XML document
      add: function (event, data) {
        $.ajax({
          url: "/signed_urls",
          type: 'GET',
          dataType: 'json',
          data: {doc: {title: data.files[0].name}}, // send the file name to the server so it can generate the key param
          async: false,
          success: function(data) {
            // Now that we have our data, we update the form so it contains all
            // the needed data to sign the request
            form.find('input[name=key]').val(data.key)
            form.find('input[name=policy]').val(data.policy)
            form.find('input[name=signature]').val(data.signature)            
          }
        })
        data.form.find('#content-type').val(file.type)
        data.submit();
      },
      send: function(e, data) {
        var filename = data.files[0].name;
        $('input[type="submit"]').val("Please wait until audio uploaded is complete..."); 
        $('#file_status').addClass('label-info').html('Uploading ' + filename);
        $('.progress').fadeIn();        
      },
      progress: function(e, data){
        // This is what makes everything really cool, thanks to that callback
        // you can now update the progress bar based on the upload progress
        var percent = Math.round((e.loaded / e.total) * 100)
        $('.bar').css('width', percent + '%')
      },
      fail: function(e, data) {
        console.log('fail')
      },
      success: function(data) {
        // Here we get the file url on s3 in an xml doc
        var url = $(data).find('Location').text()

        $('#demo_audio').val(url) // Update the real input in the other form
      },
      done: function (event, data) {
        $('input[type="submit"]').val("Create Demo");
        $('input[type="submit"]').removeAttr("disabled"); 
        $('.progress').fadeOut(300, function() {
          $('.bar').css('width', 0);
          var filename = data.files[0].name;
          $('span.filename').html(filename);     
          $('#file_status').removeClass('label-info').addClass('label-success').html(filename + ' upload complete');
          $('#file').hide();
        })
      },

    })
  })
})

signed_urls_controller.rb

class SignedUrlsController < ApplicationController
  def index
    render json: {
      policy: s3_upload_policy_document,
      signature: s3_upload_signature,
      key: "uploads/#{SecureRandom.uuid}/#{params[:doc][:title]}",
      success_action_redirect: "/"
    }
  end

  private

  # generate the policy document that amazon is expecting.
  def s3_upload_policy_document
    Base64.encode64(
      {
        expiration: 30.minutes.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
        conditions: [
          { bucket: ENV['AWS_S3_BUCKET'] },
          { acl: 'public-read' },
          ["starts-with", "$key", "uploads/"],
          { success_action_status: '201' }
        ]
      }.to_json
    ).gsub(/\n|\r/, '')
  end

  # sign our request by Base64 encoding the policy document.
  def s3_upload_signature
    Base64.encode64(
      OpenSSL::HMAC.digest(
        OpenSSL::Digest::Digest.new('sha1'),
        ENV['AWS_SECRET_ACCESS_KEY'],
        s3_upload_policy_document
      )
    ).gsub(/\n/, '')
  end
end

1 个答案:

答案 0 :(得分:5)

正如上述问题的评论部分所述,需要进行两项更改才能将上传内容的内容类型设置为audio / mpeg3。

  1. 必须更改S3 POST API调用的策略以接受其他“Content-Type”值。在示例代码中,可以通过将以下条件添加到s3_upload_policy_document方法中的条件数组来实现:["eq", "$Content-Type", "audio/mpeg3"]

  2. “Content-Type”变量必须包含在对S3的POST请求中。在jQuery文件上传器插件中,这可以通过向发送到S3的表单添加隐藏字段来实现,其名称为“Content-Type”,值为“audio / mpeg3”。