使用Rails和jQuery文件上载将Content-Type直接设置为S3上载

我在这个主题上看到过很多问题,但是我遇到过的任何问题都没有对我有用,所以我在这里张贴另一个……

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

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

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

所以我将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 = $("
"); var barContainer = $("
").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 = $("", { 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?

用以下内容替换添加块:

  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("") 

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

  1. 在我的s3预先发布的post中添加’内容类型以’开头的条款,如本页所述: http ://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html
  2. 将名为“Content-Type”的隐藏输入添加到我的表单,然后在选择文件时相应地更新它。

我相信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: '')