3

I need an interface on my Rails 3 app to upload multiple files to Amazon S3 (because i'm on heroku), possibly with progress bars.

I've easily managed how to set up paperclip and upload single files, but i'm really lost now on how to go ahead.

Please can you give me some advices? It's 2 days i'm searching across all the internet, but i can't find a working solution


** EDIT **

I really can't understand... I'm going mad 'cause I'm losing too many hours on this... please help me. If I try to open the example app cited by Johnny I only get this (and in my app it is the same):

Where is the UI?!

Where is the UI? Is there something wrong on my browser?


** EDIT 2 **

Here on GitHub you can find my testapp... please can you explain me why the damn upload UI is not showing up? Thanks!


** EDIT 3 **

Thank you very much Johnny, i wasn't aware of the fact that jquery and prototype can't live together. Now the plugin is showing up correctly, but as a try to upload something it creates a new "upload" record, but its attachment field is blank, and the files are not on s3.

This is what the console is saying:

Started POST "/uploads" for 127.0.0.1 at 2011-06-27 16:17:22 +0200
  Processing by UploadsController#create as JSON
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"GesRBTiZR1f2LV/bAeAdxWqF++gxcDJw4pPGStYGsH8=", "upload"=>{"attachment"=>[#<ActionDispatch::Http::UploadedFile:0x000001032834b8 @original_filename="animal-tiger-66550.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"upload[attachment][]\"; filename=\"animal-tiger-66550.jpg\"\r\nContent-Type: image/jpeg\r\n", @tempfile=#<File:/var/folders/Qj/QjEqvUUNGTmuki5SXOaaG++++TI/-Tmp-/RackMultipart20110627-1818-1syiex9>>]}}
  AREL (0.5ms)  INSERT INTO "uploads" ("attachment", "created_at", "updated_at", "attachment_file_name", "attachment_content_type", "attachment_file_size", "attachment_updated_at") VALUES (NULL, '2011-06-27 14:17:23.049136', '2011-06-27 14:17:23.049136', NULL, NULL, NULL, NULL)
[paperclip] Saving attachments.
Completed 200 OK in 64ms (Views: 4.2ms | ActiveRecord: 0.7ms)
Abramodj
  • 5,709
  • 9
  • 49
  • 75

4 Answers4

5

You can look at jQuery-File-Upload. Demo here and rails 3/Paperclip setup here.

Edit: As @apneadiving mentioned, the library has been updated to version 5. The script you have is for verison 4. You should try modifying this to work with PaperClip. Copy-pasting the majority of the example code into my app (with a few modifications) worked for me:


#app/public/javascripts/application.js
$(function () {
  // Initialize the jQuery File Upload widget:
  $('#fileupload').fileupload();

  // Load existing files:
  $.getJSON($('#fileupload form').prop('action'), function (files) {
    var fu = $('#fileupload').data('fileupload');
    fu._adjustMaxNumberOfFiles(-files.length);
    fu._renderDownload(files)
        .appendTo($('#fileupload .files'))
        .fadeIn(function () {
          // Fix for IE7 and lower:
          $(this).show();
         });
    });

    // Open download dialogs via iframes,
    // to prevent aborting current uploads:
    $('#fileupload .files a:not([target^=_blank])').live('click', function (e) {
      e.preventDefault();
      $('<iframe style="display:none;"></iframe>')
        .prop('src', this.href)
        .appendTo('body');
    });

});

#app/controllers/uploads_controller.rb
def create
  @upload = Upload.new(params[:upload])
  if @upload.save
    render :json => [{ 
              :pic_path => @upload.attachment.url.to_s ,
              :name => @upload.attachment.instance.attributes["picture_file_name"]
            }], :content_type => 'text/html'
  else
    render [:json => { :result => 'error'}], :content_type => 'text/html'
  end
end

#app/views/uploads/new.html.haml
  %link#theme{:href => "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/base/jquery-ui.css", :rel => "stylesheet"}
    = stylesheet_link_tag 'jquery.fileupload-ui'
    #fileupload
      = form_for Upload.new, :html => { :multipart => true } do |f|
        .fileupload-buttonbar
          %label.fileinput-button
            %span Add files...
            = f.file_field :attachment, :multiple => true
          %button.start{:type => "submit"} Start upload
          %button.cancel{:type => "reset"} Cancel upload
          %button.delete{:type => "button"} Delete files
      .fileupload-content
        %table.files
        .fileupload-progressbar
    %script#template-upload{:type => "text/x-jquery-tmpl"}
      %tr{:class => "template-upload{{if error}} ui-state-error{{/if}}"}
        %td.preview
        %td.name ${name}
        %td.size ${sizef}
        {{if error}}
        %td.error{:colspan => "2"}
          Error:
          {{if error === 'custom_failure'}}Custom Error Message
          {{else}}${error}
          {{/if}}
        {{else}}
        %td.progress
          %div
        %td.start
          %button Start
        {{/if}}
        %td.cancel
          %button Cancel
    %script#template-download{:type => "text/x-jquery-tmpl"}
      %tr{:class => "template-download{{if error}} ui-state-error{{/if}}"}
        {{if error}}
        %td
        %td.name ${name}
        %td.size ${sizef}
        %td.error{:colspan => "2"}
          Error:
          {{if error === 1}}File exceeds upload_max_filesize (php.ini directive)
          {{else}}${error}
          {{/if}}
        {{else}}
        %td.preview
          {{if thumbnail_url}}
          %a{:href => "${url}", :target => "_blank"}
            %img{:src => "${thumbnail_url}"}/
          {{/if}}
        %td.name
          <a href="${url}"{{if thumbnail_url}} target="_blank"{{/if}}>${name}
        %td.size ${sizef}
        %td{:colspan => "2"}
        {{/if}}
        %td.delete
          %button{"data-type" => "${delete_type}", "data-url" => "${delete_url}"} Delete

Edit Had a quick look at your app, the problem is that you are mixing prototype with jquery. The easiest way around this is to switch to jQuery using jquery-rails.

#Gemfile
gem 'jquery-rails'

Next, run bundle install and then rails g jquery:install.

Then change your app/views/layouts/application.erb to this:

<%= stylesheet_link_tag :all %>
<%= csrf_meta_tag %>

<%= javascript_include_tag 'jquery.min' %>
<%= javascript_include_tag 'jquery-ui-1.8.13.custom.min' %>
<%= javascript_include_tag 'jquery.tmpl.min' %>
<%= javascript_include_tag 'jquery.iframe-transport' %>
<%= javascript_include_tag 'jquery.fileupload' %>
<%= javascript_include_tag 'jquery.fileupload-ui' %>
<%= javascript_include_tag 'jquery_ujs' %>
<%= javascript_include_tag 'application' %>

Note that I removed the

<%= javascript_include_tag :defaults %>

So that I can specify the order in which jquery, jquery_ujs, and application are loaded.

David
  • 7,310
  • 6
  • 41
  • 63
  • @Johnny Grass: Indeed, jQuery File Upload is now v5, I've made a new tut here: https://github.com/blueimp/jQuery-File-Upload/wiki/Rails-setup-for-V5 – apneadiving Jun 26 '11 at 19:21
  • It is not working... the upload plugin is not showing up. I'm sure i've loaded all the .js files correctly because i've checked on safari's java console. Are you sure this solution work for you? – Abramodj Jun 27 '11 at 10:32
  • Yes what I have posted above works for me. Make sure you have the latest versions of jquery and jquery ui. – David Jun 27 '11 at 11:19
  • Oh yes, i'm sure i have both of them. What else could be my problem? Sorry, i'm new to these technologies, but i'd really like to understand what am i missing. I will upload a test project on GitHub, so you can have a look if you have time. – Abramodj Jun 27 '11 at 12:16
  • I've edited my question and inserted my test app. Please have a look at it and tell me what am i missing – Abramodj Jun 27 '11 at 13:14
  • Took a look, see my edits. You should use [Firebug](http://getfirebug.com/) to catch your javascript errors. – David Jun 27 '11 at 13:50
  • Thank you very much, your help is really appreciated. Tell me if i can do anything to give you back – Abramodj Jun 27 '11 at 14:26
  • Please can you have a look at my third edit? This situation is really frustrating for me... i feel very bad, please help me – Abramodj Jun 27 '11 at 17:59
  • I found the problem, remove `:multiple => true` from `= f.file_field :attachment` in `app/views/uploads/new`. However that means that you will have to select files individually (you should open another question on how to get around that). – David Jun 28 '11 at 09:58
  • Yes but now it is not "multiple" anymore, and so useless – Abramodj Jul 07 '11 at 16:12
  • So because of the way paperclip works(1 model, 1 attachment), one way to have `multiple => true` is by creating a child model named Photo that will store the paperclip information and associate with the upload model. You should be able to select multiple photos when creating an upload and paperclip will process each photo individually. That should solve your problem. – David Jul 07 '11 at 17:34
  • How would you do this without the jQuery File Upload plugin? Would you just loop the array in the create method? – Seed Feb 04 '14 at 03:05
1

I've begun with a very similar task recently, and the swf plugin (at least the more recent one) will indeed let you update paperclip's record. It has callbacks for just about everything you'd need to extend.

:onUploadComplete (upload_options,event)

Here's Nathan Colgate's gist on the matter. He just makes a remote call to the rails server once the upload is finished telling it of the locations for the paperclip attachment.

from his uploadCompleteHandler

var uploadCompleteHandler = function(upload_options,event){
$.ajax({
      url: '<%= notify_rails_of_successful_upload_path(:format => :js)%>',
      global: false,
      type: 'POST',
      data: ({
        'authenticity_token' : '<%= form_authenticity_token %>',
    'upload' : {
        'file_file_name' : upload_options.FileName,
            'file_file_size' : upload_options.FileSize,
            'file_content_type' : upload_options.ContentType
    }
    }),
      dataType: 'script'
   }
)
};

I'm not sure if this exact callback gets triggered for each file; it definitely looks like it would. But he passes everything paperclip needs back through an ajax request. filename,size,content-type. This way all that gets sent to heroku is some text about the file, sparing your app a good amount of work by giving it to the client.

edit: flash is the only way I've found to avoid sending a lot of data through heroku to s3. There are a few html5/js-only uploaders that might be able to get the job done, but the ones I have found are still pretty ripe on the dev tree.

winfred
  • 3,053
  • 1
  • 25
  • 16
  • I've implemented this, following the tutorial on Vimeo, but i get this error: NoMethodError (undefined method `&' for "v":String): app/controllers/s3_uploads_controller.rb:36:in `index' – Abramodj Jun 27 '11 at 09:12
0

View: (notice the array blog_post[avatars][])

<form accept-charset="UTF-8" action="/blog_posts" enctype="multipart/form-data" id="new_blog_post" method="post">
<div style="margin:0;padding:0;display:inline">
<input name="utf8" type="hidden" value="&#x2713;" />
<input name="authenticity_token" type="hidden" value="<%=form_authenticity_token %>" />
</div>
<p><input id="blog_post" name="blog_post[avatars][]" type="file" multiple /></p>
<p><input name="commit" type="submit" value="Upload" /></p>
</form>

Controller:

# POST /blog_posts
# POST /blog_posts.json
def create

    @blog_post = BlogPost.new(params[:blog_post])

    @blog_post.avatars.each do |avatar|

        each_blog_post = BlogPost.new

        each_blog_post.avatar = avatar

        if each_blog_post.save

        end

    end

end

Model:

class BlogPost < ActiveRecord::Base

    attr_accessible :title, :avatar, :avatars

    has_attached_file :avatar

    attr_accessor :avatars

end
user202448
  • 2,552
  • 5
  • 22
  • 25
0

As per Heroku support, see this.

Paperclip & multiple files upload, although not S3 specific.

Michael De Silva
  • 3,808
  • 1
  • 20
  • 24
  • The SWF plugin is not going to talk with paperclip. And the video shows a way to use standard html forms, without any progress bar – Abramodj Jun 26 '11 at 13:40