Uploadify and Rails 2.3

A few weeks back we (Steve and I) added multiple asset upload to Harmony using Uploadify. If you are thinking that sounds easy, you would be sorely mistaken. Uploadify uses flash to send the files to Rails. This isn’t a big deal except that we are using cookie sessions on Harmony and flash wasn’t sending the session information with the files, so to Rails the files appeared as unauthenticated.

We found multiple articles online showing how to get this working, but none of them worked as promised. At the time Harmony was running on Rails 2.2. Knowing that rack was probably the best way to solve our issue, we updated to 2.3, which was pretty painless, and started hacking. Be sure to check out a quick screencast of the finished product at some point as well.

Add Uploadify

First, we added the uploadify files and the following js to the assets/index view. We actually set many more options, but these are the ones pertinent to this article. Script is the url to post the files to. fileDataName is the name of the file field you would like to use. scriptData is any additional data you would like to post to the url.

<%- session_key_name = ActionController::Base.session_options[:key] -%> <script type="text/javascript"> $('#upload_files').fileUpload({ script : '/admin/assets', fileDataName : 'asset[file]', scriptData : { '<%= session_key_name %>' : '<%= u cookies[session_key_name] %>', 'authenticity_token' : '<%= u form_authenticity_token if protect_against_forgery? %>' } }); </script>

As you can see, it adds the session key and the cookie value along with the authenticity token as data that gets sent with the file. We then use a piece of rack middleware to intercept the upload and properly set the Rails session cookie.

Add Some Middleware

We created an app/middleware directory and added it to the load path in environment.rb.

%w(observers sweepers mailers middleware).each do |dir| config.load_paths << "#{RAILS_ROOT}/app/#{dir}" end

Next, we dropped flash_session_cookie_middleware.rb in the app/middleware directory.

require 'rack/utils' class FlashSessionCookieMiddleware def initialize(app, session_key = '_session_id') @app = app @session_key = session_key end def call(env) if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/ params = ::Rack::Utils.parse_query(env['QUERY_STRING']) unless params[@session_key].nil? env['HTTP_COOKIE'] = "#{@session_key}=#{params[@session_key]}".freeze end end @app.call(env) end end

And, finally, we added the following to our session_store.rb initializer.

ActionController::Dispatcher.middleware.insert_before( ActionController::Session::CookieStore, FlashSessionCookieMiddleware, ActionController::Base.session_options[:key] )

This inserts our middleware before ActionController’s CookieStore so that everything will just work as expected.

Assign the Content Type

The only other thing we needed to do was manually set the content type of the file. We were using paperclip (which is awesome) to do uploads, so something like this did the trick:

@asset.file_content_type = MIME::Types.type_for(@asset.original_filename).to_s

Be sure to add the mime type gem to your environment.rb file as well.

config.gem 'mime-types', :lib => 'mime/types'

But Why?

So why did we go through all this trouble to allow multiple uploads at once? Taking a quick look at the finished product might help. I didn’t record the entire screen in the video, as we haven’t actually released Harmony yet (ooooh secrets!), but I did capture enough that you can see the awesome uploads in action.

Hope this spares some other poor soul attempting the same thing some time.