Rails直接上传到Amazon S3

问题描述

| 我希望为我的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上都有很多此类配置,例如这个。     

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...