Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Direct Uploads to S3 using Carrierwave

I've recently converted the below from using Paperclip to Carrierwave uploading to Amazon S3 so I can make use of the carrierwave_direct gem and then Sidekiq or another background processing gem.

class Release < ActiveRecord::Base
  has_many :releases_tracks, :dependent => :destroy
  has_many :tracks, :through => :releases_tracks, :order => "releases_tracks.position DESC"
  accepts_nested_attributes_for :tracks, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => :true
  accepts_nested_attributes_for :releases_tracks  
end

class Track < ActiveRecord::Base
  mount_uploader :track, TrackUploader
  has_many :releases_tracks, :dependent => :destroy
  has_many :releases, :through => :releases_tracks 
end

/views/releases/track_upload.html.erb

<%= form_for(@release, :html => { :multipart => true }) do |f| %>
<h3>Upload Tracks for <%= @release.title %></h3>
    <% index = 0 %>
    <%= f.fields_for :tracks do |builder| %>
    <%= @release.tracks[index].name %>
        <%= f.file_field :track, :class => "file styled", :title => 'Select Track'%>
    <% index += 1 %>
    <% end %>

    <%= f.submit "Upload Tracks", :class => "submit" %>
<% end %>

Carrierwave uploads are working, but I can't figure out how to get the direct part working. Partly because I can't figure out how to incorporate the suggested form code:

<%= direct_upload_form_for @uploader do |f| %>
  <%= f.file_field :track %>
  <%= f.submit %>
<% end %

Or the where in my track OR release controller I place the suggested:

@uploader = User.new.track
@uploader.success_action_redirect = new_user_url

The readme https://github.com/dwilkie/carrierwave_direct and Railscast http://railscasts.com/episodes/383-uploading-to-amazon-s3 both point towards uploading your file first and then creating your database entry. In my app the db entries already exist. The Railscast does say it's possible but doesn't go through it. So that's the first problem.

The second is that I need to upload more than one file at a time. The code above does achieve that, but very slowly and it of course renders my app pretty useless as it does so.

Can anyone help? Massive thanks in advance!

like image 507
Raoot Avatar asked Oct 04 '12 10:10

Raoot


1 Answers

First of all I would advise you not to use carrierwave_direct, I really don't like this gem, for many reasons. One of those reasons, is that as it's said in the docs

Please be aware that this gem only supports single file uploads. If you want to upload multiple files simultaneously you'll have to use a javascript or flash uploader.

But if you want to use it, here is what I guess you have to do :

So first about the

@uploader = User.new.track
@uploader.success_action_redirect = new_user_url

It seems you are trying to upload tracks, and as you said your models have already been created, I guess your are trying to upload new tracks for an existing release. Correct me if I'm wrong.

so you should create the @uploader var in the #track_upload method of your ReleasesController.

class ReleasesController
  ...  
  def track_update 
    @uploader = User.new.track
    @uploader.success_action_redirect = new_user_url
  end
  ...
end

then in the associated view (/views/releases/track_upload.html.erb), you can use the direct_upload_form

<%= direct_upload_form_for @uploader do |f| %>
  <%= f.file_field :track %>
  <%= f.submit %>
<% end %>

The form will upload the file directly to s3, just after you selected the file. Then I don't know exactly how, but carrierwave_direct should give you back the url of the uploaded file. I'm not sure about that as I've never been that far with it, but the idea is that your file just got uploaded to s3, now it has to be linked to your model, so the file doesn't get 'lost'. So maybe carrierwave_direct is doing things on its own (I doubt that ...) by doing some ajax requests or anything else.

Anyway as you want to upload more than one file, I'd like to point you to a tutorial I recently wrote

This shows how to upload files directly to s3, without carrierwave_direct, but by doing things on your own. This requires a little bit more code and time, but you have more control about what's happening.

In your case, you'll want to put the form I'm using in my tutorial in your view, in the /views/releases/track_upload.html.erb view. Then once you'll select a file, the successful AJAX request(emitted by the jQueryFileUpload plugin) will give you the URL of the uploaded file so you can save it in your Track model (you'll probably want to emit a new AJAX request to your server to create the new Track model, or to populate an other form on the page, like the one you were using in the /views/releases/track_upload.html.erb file, and then the tracks will be saved on submit.) I'm not sure I'm really clear about that, let me know if you need more explanations.

And the good thing about that is that if you simply add multiple to your file input, then the awesome jQueryFileUpload plugin will send a request per file to s3, then you'll get the URL's of the uploaded files in each ajax results :D

And you can tweak things to add progress bars, and things like that with the jQuery plugin, you can really create awesome things.

Hope it'll help you !

like image 155
pjam Avatar answered Sep 21 '22 01:09

pjam