Plupload is an open source javascript upload handler that supports uploading files directly to Amazon S3. This is an alternative to uploading files to the web server, and then to S3. You will need to use the Flash or Silverlight options to upload directly to S3 because Amazon has yet to enable cross-origin uploading.
In researching how to use Plupload with Rails, I came across this excellent Rails3-S3-Uploader-Pluplod sample Rails 3 application. It very nicely lays out what needs to be done to combine Plupload, Rails, and direct S3 uploads. You should pay particular attention to the uploads_helper.rb file where the bulk of the Plupload code is located. Also be sure to upload a publicly readable crossdomain.xml to the root directory of your S3 bucket to give Flash access to the bucket.
In my implementation I chose to break the javascript out into a separate file, instead of it being generated in a helper. I also added an event handler for the FileUploaded event so the page could react to the file upload completion.
uploader.bind('FileUploaded', function(up, file) { // Function that POSTs an ajax request to the server // with the name of the new file fileUploaded(file.name); }); |
The Rails application will receive the POST indicating that the image has been uploaded to S3. The controller then needs to instantiate a Paperclip model. An example of setting up a Paperclip model using the S3 storage option can be seen in Dogan Kaya Berktas’ post Amazon S3 and Paperclip on Rails 3.
The trick here is that unlike with a normal file upload, we don’t have a temp file on the local web server – instead it is sitting in an S3 bucket. There are several ways of handling this. One is described in this Rails Toolkit post. I chose to go a different route; I am instead instantiating a custom wrapper and passing that into Paperclip.
class Thing < ActiveRecord::Base has_attached_file :attachment, :path => "/uploads/:style/:basename.:extension", :storage => :s3, :s3_credentials => "#{Rails.root}/config/s3.yml", :styles => { :thumb => "180x180#", # Square thumbnails :original => "405", # Resize to a max width for originals } end |
class UploadedFile def initialize(file_name) @file_name = file_name encoded_filename = CGI::escape(file_name) @io = open("http://s3.amazonaws.com/my_bucket/uploads/#{encoded_filename}") end def method_missing(method_name, *args) @io.send method_name, *args end def respond_to?(method) super || @io.respond_to?(method) end def original_filename @file_name end def content_type @content_type ||= MIME::Types.type_for(@file_name) end end |
In a controller (or in a background job) you can then create the Thing that has the attachment, and Paperclip will download the file from S3 before doing its processing and then uploading the processed images back to S3.
def create uploaded_file = UploadedFile.new params[:file_name] @thing = Thing.create!(:attachment => uploaded_file) end |


5 Comments
Hi Patrick,
Thank you for this work through, its given me some thoughts into managing file processing post upload. I was wondering if you had a working example project of the above somewhere as I am trying to put 2 and 2 together and am struggling a little?
Cheers,
Simon
Simon,
Unfortunately the project I was working on is not open source, and I don’t currently have any kind of an example project that goes along with this post.
If you have any specific questions I might be able to help you out.
It took me a while to discover, but in Ruby 1.9.x you must include
require 'open-uri'in theUploadedFileclass or the call toopen()will fail.Also, thank you Patrick for this blog post, it was incredibly helpful
John, thanks for the 1.9 tip. Much appreciated.