问题描述
|
我希望为我的Rails应用程序添加功能,以将文件直接上传到Amazon S3。根据我的研究,普遍的共识似乎是使用s3-swf-upload-plugin。我已经使用该gem设置了一个示例应用程序,但仅允许选择一个文件就无法使其正常运行。我还想创建一个上传记录并使用回形针创建一个缩略图,对此我几乎找不到指导。
所以我的问题是:
(1)我在使用该宝石的方向正确吗?还是我应该再使用另一个宝石?
(2)是否有可供我参考的样本?
任何帮助将不胜感激。
克里斯
解决方法
尝试使用名为CarrierWaveDirect的新Gem,它可让您使用html表单将文件直接上传到S3,并将图像处理轻松地移至后台进程
,不确定是否可以轻松地修改它一次仅上传一个文件,但是这个gem对我来说很好用。它基于Ryan Bates的Railscast之一:
https://github.com/waynehoover/s3_direct_upload
,尝试研究载波https://github.com/jnicklas/carrierwave(支持s3)
使用carrierwave上传多文件,并在http://blog.assimov.net/post/4306595758/multi-file-upload-with-uploadify-and-carrierwave-on上上传
,如果您使用的是Rails 3,请查看我的示例项目:
使用Rails 3,基于Flash和基于MooTools的FancyUploader将示例项目直接上传到S3:https://github.com/iwasrobbed/Rails3-S3-Uploader-FancyUploader
使用Rails 3,Flash / Silverlight / GoogleGears / BrowserPlus和基于jQuery的Plupload的示例项目可直接上传到S3:https://github.com/iwasrobbed/Rails3-S3-Uploader-Plupload
顺便说一句,您可以使用Paperclip进行后期处理,就像这篇博客文章描述的那样:
http://www.railstoolkit.com/posts/fancyupload-amazon-s3-uploader-with-paperclip
,我已经在Rails中使用了Heroku的直接到S3上传解决方案(它使用jQuery-File-Upload和aws-sdk gem),因此可以使用ajax远程上传到S3。我希望这是有用的:
posts_controller.rb
before_action :set_s3_direct_post,only: [:index,:create]
before_action :delete_picture_from_s3,only: [:destroy]
class PostsController < ApplicationController
def index
.
.
end
def create
@post = @user.posts.build(post_params)
if @post.save
format.html
format.js
end
end
def destroy
Post.find(params[:id]).destroy
end
private
def set_s3_direct_post
return S3_BUCKET.presigned_post(key: \"uploads/#{SecureRandom.uuid}/${filename}\",success_action_status: \'201\',acl: \'public-read\')
end
def delete_picture_from_s3
key = params[:picture_url].split(\'amazonaws.com/\')[1]
S3_BUCKET.object(key).delete
return true
rescue => e
# If anyone knows a good way to deal with a defunct file sitting in the bucket,please speak up.
return true
end
def post_params
params.require(:post).permit(:content,:picture_url)
end
end
posts.html.erb
<div class=\"info\" data-url=\"<%= @s3_direct_post.url %>\"
data-formdata=\"<%= (@s3_direct_post.fields.to_json) %>\"
data-host=\"<%= URI.parse(@s3_direct_post.url).host %>\">
</div>
表格
<%= form_for(:post,url: :posts,method: :post,html: { class: \"post_form\",id: \"post_form-#{post.id}\" }
) do |f| %>
<%= f.text_area :content,id: \"postfield-#{post.id}\",class: \"postText\" %>
<%= f.button( :submit,name: \"Post\",title: \"Post\" ) do %>
<span class=\"glyphicon glyphicon-ok\" aria-hidden=\"true\"></span>
<% end %>
<span class=\"postuploadbutton\" id=\"postUp-<%= post.id %>\" title=\"Add file\" >
<span class=\"glyphicon glyphicon-upload\" aria-hidden=\"true\"></span>
</span>
<span title=\"Cancel file\" class=\"noticecancelupload\" id=\"postCancel-<%= post.id %>\" >
<span class=\"glyphicon glyphicon-remove-circle\" aria-hidden=\"true\"></span>
</span>
<%= f.file_field :picture_url,accept: \'image/jpeg,image/gif,image/png\',class: \"notice_file_field\",id: \"postFile-#{post.id}\" %>
<% end %>
_post.html.erb
<%= button_to post_path(
params: {
id: post.id,picture_url: post.picture_url
}
),class: \'btn btn-default btn-xs blurme\',data: { confirm: \"Delete post: are you sure?\" },method: :delete do %>
<span class=\"glyphicon glyphicon-remove\" aria-hidden=\"true\"></span>
<% end %>
每个_post.html.erb中的Javascript
$(document).off(\'click\',\"#postUp-<%= post.id %>\");
$(document).on(\'click\',\'#postUp-<%= post.id %>\',function(e) {
prepareUpload(\"#post_form-<%= post.id %>\");
$(\'#postFile-<%= post.id %>\').trigger(\"click\");
});
$(document).off(\'click\',\"#postCancel-<%= post.id %>\");
$(document).on(\'click\',\'#postCancel-<%= post.id %>\',function(e) {
$(\".appendedInput\").remove(); // $(\'#postFile-<% post.id %>\').val(\"\"); doesn\'t work for me
$(\'.progBar\').css(\'background\',\'white\').text(\"\");
});
$(document).off(\'submit\',\"#post_form-<%= post.id %>\"); // without this the form submitted multiple times in production
$(document).on(\'submit\',\'#post_form-<%= post.id %>\',function(e) { // don\'t use $(\'#post_form-<%= post.id %>\').submit(function() { so it doesn\'t bind to the #post_form (so it still works after ajax loading)
e.preventDefault(); // prevent normal form submission
if ( validatePostForm(\'<%= post.id %>\') ) {
$.ajax({
type: \'POST\',url: $(this).attr(\'action\'),data: $(this).serialize(),dataType: \'script\'
});
$(\'#postCancel-<%= post.id %>\').trigger(\"click\");
}
});
function validatePostForm(postid) {
if ( jQuery.isBlank($(\'#postfield-\' + postid).val()) && jQuery.isBlank($(\'#postFile-\' + postid).val()) ) {
alert(\"Write something fascinating or add a picture.\");
return false;
} else {
return true;
}
}
application.js中的Javascript
function prepareUpload(feckid) {
$(feckid).find(\"input:file\").each(function(i,elem) {
var fileInput = $(elem);
var progressBar = $(\"<div class=\'progBar\'></div>\");
var barContainer = $(\"<div class=\'progress\'></div>\").append(progressBar);
fileInput.after(barContainer);
var maxFS = 10 * 1024 * 1024;
var info = $(\".info\");
var urlnumbnuts = info.attr(\"data-url\");
var formdatanumbnuts = jQuery.parseJSON(info.attr(\"data-formdata\"));
var hostnumbnuts = info.attr(\"data-host\");
var form = $(fileInput.parents(\'form:first\'));
fileInput.fileupload({
fileInput: fileInput,maxFileSize: maxFS,url: urlnumbnuts,type: \'POST\',autoUpload: true,formData: formdatanumbnuts,paramName: \'file\',dataType: \'XML\',replaceFileInput: false,add: function (e,data) {
$.each(data.files,function (index,file) {
if (file.size > maxFS) {
alert(\'Alas,the file exceeds the maximum file size of 10MB.\');
form[0].reset();
return false;
} else {
data.submit();
return true;
}
});
},progressall: function (e,data) {
var progress = parseInt(data.loaded / data.total * 100,10);
progressBar.css(\'width\',progress + \'%\')
},start: function (e) {
progressBar.
css(\'background\',\'orange\').
css(\'display\',\'block\').
css(\'width\',\'0%\').
text(\"Preparing...\");
},done: function(e,data) {
var key = $(data.jqXHR.responseXML).find(\"Key\").text();
var url = \'//\' + hostnumbnuts + \'/\' + key;
var input = $(\'<input />\',{ type:\'hidden\',class:\'appendedInput\',name: fileInput.attr(\'name\'),value: url });
form.append(input);
progressBar.
css(\'background\',\'green\').
text(\"Ready\");
},fail: function(e,data) {
progressBar.
css(\"background\",\"red\").
css(\"color\",\"black\").
text(\"Failed\");
}
});
});
} // function prepareUpload()
create.js.erb
$(\".info\").attr(\"data-formdata\",\'<%=raw @s3_direct_post.fields.to_json %>\'); // don\'t use .data() to set attributes
$(\".info\").attr(\"data-url\",\"<%= @s3_direct_post.url %>\");
$(\".info\").attr(\"data-host\",\"<%= URI.parse(@s3_direct_post.url).host %>\");
$(\'.post_form\')[0].reset();
$(\'.postText\').val(\'\');
application.js
//= require jquery-fileupload/basic
config / initializers / aws.rb
Aws.config.update({
region: \'us-east-1\',credentials: Aws::Credentials.new(ENV[\'AWS_ACCESS_KEY_ID\'],ENV[\'AWS_SECRET_ACCESS_KEY\']),})
S3_BUCKET = Aws::S3::Resource.new.bucket(ENV[\'S3_BUCKET\'])
笔记:
此解决方案专为index.html.erb页面上的多种邮寄表单而设计。这就是为什么将“ 9”信息放置在index.html.erb中的“ 10”类的div内,而不是在每个帖子形式中放置的原因。这意味着无论页面上有多少表格,任何时候页面上都只会出现一个“ 9”。单击文件上传按钮后,@s3_direct_post
中的数据才被抓取(调用prepareUpload()
)。提交后,将在posts控制器中生成一个新的@s3_direct_post
,并通过create.js.erb更新.info
中的信息。将“ 9”数据存储在表单中意味着“ 9”的许多不同实例可以同时存在,从而导致文件名生成错误。
您需要在posts controller index操作(准备第一次上传)和create操作(准备第二次及以后上传)中都按“ 18”。
e.preventDefault();
阻止了常规表单的提交,因此可以$.ajax({
“手动”完成提交。为什么不只使用表格中的remote: true
?因为在Rails中,文件上传是通过HTML请求完成的,即使您尝试远程执行,页面刷新也是如此。
使用info.attr()
而不是info.data()
来设置和检索@s3_direct_post
属性,因为info.data不会更新
(例如,请参见此问题)。这意味着您还必须使用25手动将属性解析为对象(.data()实际上会自动执行)。
不要在application.js中使用ѭ26。该错误是一个真正的问题(请参阅此处)。直到我对此进行了更改,原始的Heroku解决方案才起作用。
,您可以使用Paperclip上载到S3(请参阅文档)并创建缩略图,尽管先将其上载到临时文件夹,然后再应用图像处理,然后再将文件上载到S3。
至于这种配置的示例,在整个Blog圈和StackOverflow上都有很多此类配置,例如这个。