Ruby on rails 3 Rails-使用Jquery文件上传功能将大文件直接上传到S3(托管在Heroku上)
我使用的是Heroku,这意味着我必须直接将多个大文件上传到S3。。我使用的是Rails 3.2.11和Ruby 1.9.3。我不希望使用carrierwave或回形针宝石,或者在这一点上真的改变太多-我只需要让这一切我有工作 在尝试转到S3之前,如果我在本地运行我的应用程序,我可以将多个大文件上传到本地文件系统。当我在Heroku上运行它时,小文件上传,但大文件上传失败。因此切换到S3 我尝试了一些调整,也尝试了下面的链接,但这对我已经使用的本地服务器的文件系统(还有Heroku,但是Heroku无法处理大文件)来说是一个太大的改变 尝试: 我在这里尝试了一些关于堆栈溢出的其他示例,但是它们对本地工作的更改太多了,而且,我没有掌握它们所做的一切 现在,当我尝试上传图像时会发生什么 这就好像文件上传可以正常工作一样——预览图像已经成功创建,但是没有任何东西被上传到Amazon s3,我也没有收到任何类型的错误消息(比如s3身份验证失败或者其他什么..什么都没有) 为了将文件转移到我的s3存储,我需要做哪些更改?我可以向控制台写些什么来检测连接到我的s3的问题(如果有) 我的表格:Ruby on rails 3 Rails-使用Jquery文件上传功能将大文件直接上传到S3(托管在Heroku上),ruby-on-rails-3,heroku,amazon-s3,jquery-file-upload,Ruby On Rails 3,Heroku,Amazon S3,Jquery File Upload,我使用的是Heroku,这意味着我必须直接将多个大文件上传到S3。。我使用的是Rails 3.2.11和Ruby 1.9.3。我不希望使用carrierwave或回形针宝石,或者在这一点上真的改变太多-我只需要让这一切我有工作 在尝试转到S3之前,如果我在本地运行我的应用程序,我可以将多个大文件上传到本地文件系统。当我在Heroku上运行它时,小文件上传,但大文件上传失败。因此切换到S3 我尝试了一些调整,也尝试了下面的链接,但这对我已经使用的本地服务器的文件系统(还有Heroku,但是Hero
<%= form_for @status do |f| %>
{A FEW HTML FIELDS USED FOR A DESCRIPTION OF THE FILES - NOT IMPORTANT FOR THE QUESTION}
File:<input id="fileupload" multiple="multiple" name="image"
type="file" data-form-data = <%= @s3_direct_post.fields%>
data-url= <%= @s3_direct_post.url %>
data-host =<%=URI.parse(@s3_direct_post.url).host%> >
<%= link_to 'submit', "#", :id=>'submit' , :remote=>true%>
<% end %>
我的控制器:
def index
#it's in the index just to simplify getting it working
@s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read')
end
为表单生成的元素是(通过Inspect元素):
救命啊 对于S3,实际上没有现成的上传文件的解决方案,因为Amazon是一个相当复杂的工具 当时我也遇到了类似的问题,花了两周时间试图弄清楚S3是如何工作的,现在我使用一个可行的解决方案将文件上传到S3上。我可以告诉你一个对我有效的解决方案,我从未尝试过Heroku提出的方案。 我选择使用的插件是Plupload,因为它是我实际使用的唯一组件,除了通过XHR直接上传S3外,它还提供百分比指示器和浏览器内图像大小调整,我发现这对于生产应用程序是完全必需的,其中一些用户有20mb的图像,他们想上传作为他们的头像 S3中的一些基础知识: 步骤1 Amazon bucket需要在其CORS文件中进行正确配置,以便首先允许外部上传。Heroku totorial已经告诉您如何将配置放在正确的位置。 步骤2 需要策略数据,否则客户端将无法访问相应的bucket文件。我发现通过Ajax调用生成策略更好,例如,admin可以将文件上传到不同用户的文件夹中。 在我的示例中,cancan用于管理给定用户的安全性,figaro用于管理ENV变量
def aws_policy_image
user = User.find_by_id(params[:user_id])
authorize! :upload_image, current_user
options = {}
bucket = Rails.configuration.bucket
access_key_id = ENV["AWS_ACCESS_KEY_ID"]
secret_access_key = ENV["AWS_SECRET_ACCESS_KEY"]
options[:key] ||= "users/" + params[:user_id] # folder on AWS to store file in
options[:acl] ||= 'private'
options[:expiration_date] ||= 10.hours.from_now.utc.iso8601
options[:max_filesize] ||= 10.megabytes
options[:content_type] ||= 'image/' # Videos would be binary/octet-stream
options[:filter_title] ||= 'Images'
options[:filter_extentions] ||= 'jpg,jpeg,gif,png,bmp'
policy = Base64.encode64(
"{'expiration': '#{options[:expiration_date]}',
'conditions': [
{'x-amz-server-side-encryption': 'AES256'},
{'bucket': '#{bucket}'},
{'acl': '#{options[:acl]}'},
{'success_action_status': '201'},
['content-length-range', 0, #{options[:max_filesize]}],
['starts-with', '$key', '#{options[:key]}'],
['starts-with', '$Content-Type', ''],
['starts-with', '$name', ''],
['starts-with', '$Filename', '']
]
}").gsub(/\n|\r/, '')
signature = Base64.encode64(
OpenSSL::HMAC.digest(
OpenSSL::Digest::Digest.new('sha1'),
secret_access_key, policy)).gsub("\n", "")
render :json => {:access_key_id => access_key_id, :policy => policy, :signature => signature, :bucket => bucket}
end
我甚至把这个方法放到了应用程序控制器中,尽管您可以为它找到更好的地方。
当然,这个函数的路径应该放在路由中
步骤3
前端,获取plupload:制作一些链接作为上传按钮:
<a id="upload_button" href="#">Upload</a>
can\u render
变量非常有用,这样您就可以使应用程序在实际完成上载后重新呈现页面
要使按钮在其他地方工作,请拨打:
ImageUploader.photo_uploader(user_id);
该按钮将充当多功能上传器按钮。
重要的是,制定政策的方式确保没有人可以将照片上传到其他人的目录中。
如果有一个版本不是通过ajax回调实现同样的功能,而是通过web钩子实现同样的功能,那就太好了,这是我将来想要做的事情
同样,这不是一个完美的解决方案,但从我的经验来看,它足以将图像和视频上传到亚马逊
注意如果有人问我为什么有这种复杂的面向对象的上传器对象结构,原因是我的应用程序有各种不同的上传器,它们的行为不同,它们需要有一个具有共同行为的初始值设定项。按照我的方式,我可以用最少的代码为视频编写一个初始值设定项,这将与现有的图像上传程序做类似的事情。cool。。我不确定这对我有什么帮助,但我会调查的。。非常感谢,我喜欢你的面向对象解决方案。。
<a id="upload_button" href="#">Upload</a>
function Plupload(config_x, access_key_id, policy, signature, bucket) {
var $this = this;
$this.config = $.extend({
key: 'error',
acl: 'private',
content_type: '',
filter_title: 'Images',
filter_extentions: 'jpg,jpeg,gif,png,bmp',
select_button: "upload_button",
multi_selection: true,
callback: function (params) {
},
add_files_callback: function (up, files) {
},
complete_callback: function (params) {
}
}, config_x);
$this.params = {
runtimes: 'html5',
browse_button: $this.config.select_button,
max_file_size: $this.config.max_file_size,
url: 'https://' + bucket + '.s3.amazonaws.com/',
flash_swf_url: '/assets/plupload/js/Moxie.swf',
silverlight_xap_url: '/assets/plupload/js/Moxie.xap',
init: {
FilesRemoved: function (up, files) {
/*if (up.files.length < 1) {
$('#' + config.select_button).fadeIn('slow');
}*/
}
},
multi_selection: $this.config.multi_selection,
multipart: true,
// resize: {width: 1000, height: 1000}, // currently causes "blob" problem
multipart_params: {
'acl': $this.config.acl,
'Content-Type': $this.config.content_type,
'success_action_status': '201',
'AWSAccessKeyId': access_key_id,
'x-amz-server-side-encryption': "AES256",
'policy': policy,
'signature': signature
},
// Resize images on clientside if we can
resize: {
preserve_headers: false, // (!)
width: 1200,
height: 1200,
quality: 70
},
filters: [
{
title: $this.config.filter_title,
extensions: $this.config.filter_extentions
}
],
file_data_name: 'file'
};
$this.uploader = new plupload.Uploader($this.params);
$this.uploader.init();
$this.uploader.bind('UploadProgress', function (up, file) {
$('#' + file.id + ' .percent').text(file.percent + '%');
});
// before upload
$this.uploader.bind('BeforeUpload', function (up, file) {
// optional: regen the filename, otherwise the user will upload image.jpg that will overwrite each other
var extension = file.name.split('.').pop();
var file_name = extension + "_" + (+new Date);
up.settings.multipart_params.key = $this.config.key + '/' + file_name + '.' + extension;
up.settings.multipart_params.Filename = $this.config.key + '/' + file_name + '.' + extension;
file.name = file_name + '.' + extension;
});
// shows error object in the browser console (for now)
$this.uploader.bind('Error', function (up, error) {
console.log('Expand the error object below to see the error. Use WireShark to debug.');
alert_x(".validation-error", error.message);
});
// files added
$this.uploader.bind('FilesAdded', function (up, files) {
$this.config.add_files_callback(up, files, $this.uploader);
// p(uploader);
// uploader.start();
});
// when file gets uploaded
$this.uploader.bind('FileUploaded', function (up, file) {
$this.config.callback(file);
up.refresh();
});
// when all files are uploaded
$this.uploader.bind('UploadComplete', function (up, file) {
$this.config.complete_callback(file);
up.refresh();
});
}
Plupload.prototype.init = function () {
//
}
ImageUploader = {
init: function (user_id, config, callback) {
$.ajax({
type: "get",
url: "/aws_policy_image",
data: {user_id: user_id},
error: function (request, status, error) {
alert(request.responseText);
},
success: function (msg) {
// set aws credentials
callback(config, msg);
}
});
},
},
// local functions
photo_uploader: function (user_id) {
var container = "#photos .unverified_images" // for example;
var can_render = false;
this.init(user_id,
{
select_button: "upload_photos",
callback: function (file) {
file.aws_id = file.id;
file.id = "0";
file.album_title = "userpics"; // I use this param to manage photo directory
file.user_id = user_id;
//console.log(file);
[** your ajax code here that saves the image object in the database via file variable you get here **]
});
},
add_files_callback: function (up, files, uploader) {
$.each(files, function (index, value) {
// do something like adding a progress bar html
});
uploader.start();
},
complete_callback: function (files) {
can_render = true;
}
}, function (config, msg) {
config.key = "users/" + user_id;
// Most important part:
window.photo_uploader = new Plupload(config, msg.access_key_id, msg.policy, msg.signature, msg.bucket);
});
}
ImageUploader.photo_uploader(user_id);