【问题标题】:Setting the Content-Type in direct to S3 upload using Rails and jQuery File Upload使用 Rails 和 jQuery File Upload 将 Content-Type 直接设置为 S3 上传
【发布时间】:2023-04-02 05:23:01
【问题描述】:

我在这里看到了很多关于这个主题的问题,但我遇到的没有一个对我有用,所以我在这里发布另一个......

我在 Ruby on Rails 上尝试使用 jQuery File Upload plugin 配置文件上传直接到 Amazon S3。我跟着很有帮助的Heroku tutorial 一起完成了初始设置。文件上传正常,但在 S3 中都标记为Content-Type: binary/octet-stream,所以当它们在应用程序中提供时,所有文件都会下载而不是直接打开。

这是一个问题,因为我试图允许图像、PDF、音频或视频文件,所以我需要能够从文件中获取正确的Content-Type 并将其传递给 S3。在查看亚马逊上的 AWS-SDK gem 文档时,我看到 this section 关于将 .where(:content_type).starts_with("") 添加到预签名的 post 对象的末尾以修改策略。但是,当我这样做时,它抛出了一个错误:

<Error><Code>AccessDenied</Code>
<Message>Invalid according to Policy: Policy Condition failed: ["starts-with", "$Content-Type", ""]</Message>

所以我将content_type: "" 添加到预签名post 对象的opts 哈希中,现在它又可以工作了,但不是所有文件都默认为binary/octet-stream,它们都默认为image/jpeg。这是我现在的代码:

控制器

def new
  @s3_direct_post = S3_BUCKET.presigned_post(
                  key: "uploads/#{SecureRandom.uuid}/${filename}",
                  success_action_status: 201,
                  acl: :public_read,
                  content_type: "").where(:content_type).starts_with("")
end

_form.html.haml

:javascript
  $(function() {
    $('.directUpload').find("input:file").each(function(i, elem) {
      var fileInput    = $(elem);
      var form         = $(fileInput.parents('form:first'));
      var submitButton = form.find('input[type="submit"]');
      var progressBar  = $("<div class='bar'></div>");
      var barContainer = $("<div class='progress'></div>").append(progressBar);
      var fd           = #{@s3_direct_post.fields.to_json.html_safe};
      fileInput.after(barContainer);
      fileInput.fileupload({
        // This 'add' section is where I thought to set the Content-Type, but I've tried  with and without it and Content-Type remains the same on S3
        add: function (e, data) {
          fd["Content-Type"] = data.files[0].type;  
          console.log(fd);    // The JSON object shows Content-Type correctly in console
          data.submit();
        },  
        fileInput:        fileInput,
        url:              '#{@s3_direct_post.url}',
        type:             'POST',
        autoUpload:       true,
        formData:         fd,   // My updated JSON object
        paramName:        'file',
        dataType:         'XML',
        replaceFileInput: false,
        progressall: function (e, data) {
          var progress = parseInt(data.loaded / data.total * 100, 10);
          progressBar.css('width', progress + '%')
        },
        start: function (e) {
          submitButton.prop('disabled', true);

          progressBar.
            css('background', 'green').
            css('display', 'block').
            css('width', '0%').
            text("Loading...");
        },
        done: function(e, data) {
          submitButton.prop('disabled', false);
          progressBar.text("Uploading done");

          // extract key and generate URL from response
          var key   = $(data.jqXHR.responseXML).find("Key").text();
          var url   = 'https://d295xbrl26r3ll.cloudfront.net/' + key.replace(/ /g, "%20");

          // create hidden field
          var input = $("<input />", { type:'hidden', name: 'item[file_url]', value: url })
          form.append(input);
        },
        fail: function(e, data) {
          submitButton.prop('disabled', false);

          progressBar.
            css("background", "red").
            text("Failed");
        }
      });
    });
  });

如何将Content-Type 正确发送到 S3?

【问题讨论】:

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


    【解决方案1】:

    我相信 aws-sdk (2.9.6) 的最新语法是:

    @s3_direct_post = S3_BUCKET.presigned_post(
                      key: "uploads/#{SecureRandom.uuid}/${filename}",
                      success_action_status: 201,
                      acl: 'public-read',
                      content_type_starts_with: '')
    

    【讨论】:

      【解决方案2】:

      如果有人正在寻找这个问题的最新答案,我可以通过以下方式解决这个问题:

      1. 在我的 s3 预签名帖子中添加“内容类型开头为”子句,如本页所述:http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
      2. 向我的表单添加一个名为“Content-Type”的隐藏输入,然后在选择文件时相应地更新它。

      【讨论】:

        【解决方案3】:

        将您的添加块替换为:

              fd["Content-Type"] = data.files[0].type;  
              data.formData = fd;
              data.submit();
        

        回调是正确的,但是data.formData已经取了fd的原始版本。只需使用修改后的 fd 再次设置它,就可以开始了。

        也更新控制器方法,这样您就不会重复两次:

        @s3_direct_post = S3_BUCKET.presigned_post(
                          key: "uploads/#{SecureRandom.uuid}/${filename}",
                          success_action_status: 201,
                          acl: :public_read).where(:content_type).starts_with("")
        

        【讨论】:

        • 谢谢!像魅力一样工作。
        • ie家庭浏览器怎么样?似乎他们不太支持这个 File api,有没有解决方法?
        • 可能插件变了?我在 9.14.1 上,传递给 add 方法的 data 对象不再提供对 .formData 的访问。更改“内容类型”
        猜你喜欢
        • 2014-08-22
        • 2015-12-22
        • 1970-01-01
        • 2015-11-09
        • 1970-01-01
        • 2015-06-21
        • 1970-01-01
        • 1970-01-01
        • 2018-06-01
        相关资源
        最近更新 更多