【发布时间】:2013-08-02 13:29:13
【问题描述】:
我需要通过客户端 jQuery 上传表单向亚马逊提供内容类型的帮助。我需要添加内容类型,因为我上传的音频文件不会在 ie10 的 jPlayer 中播放,除非正确设置了内容类型。我使用 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>
音频上传.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
【问题讨论】:
-
向表单添加一个名为“Content-Type”的隐藏输入和一个与文件的 MIME 类型匹配的值应该可以工作,但您还需要修改策略(您的 /signed_urls 脚本) S3 接受您的请求。最好使用 Fiddler 或 Charles 等网络调试工具调查发送到 S3 的实际请求和响应,以便更好地了解正在发生的事情。
-
有趣,我从没想过要更新它。我显然需要在 S3 上做更多的功课。我在我的问题中添加了返回签名的控制器的代码。那么在该控制器的 s3_upload_policy_document 方法中,将新字段添加到条件数组中?
-
是的,在条件数组中添加类似 ["eq", "$Content-Type", "audio/mpeg3"] 的内容。当 S3 停止响应“根据策略无效:...”之类的内容时,您就会知道自己做对了。
-
非常感谢@dcro,成功了。并且在上传时设置内容类型使音频可以在生产中播放! :) 如果您将您的评论变成答案,我很乐意接受。
-
不客气,我很高兴你能够让它工作。我已经添加了上面的信息作为答案。
标签: ruby-on-rails amazon-s3 jquery-file-upload