As long as HTML5 is not production-ready across all main browsers, file uploads are still a painful thing. See a demonstration about how it will be one day here.

But back to today: I’m not really a friend of flash, but in the moment it’s nearly the only possibility to get multiple-file-uploads and progress-bars. Best practice to do this is to use Uploadify, which is a jQuery plugin that allows the easy integration of a multiple (or single) file uploads on your website. It requires Flash and is compatible with any backend development language. This flash uploader sends the data to our rails controller where they are saved with the glorious help of Paperclip.

Most HOWTOs on the web are for Rails 2.x – and no one solved my former problem to get a cookie/session through Flash. So here is my working Rails 3 setup:

Patch Rack-Middleware

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/
      req = Rack::Request.new(env)
      env['HTTP_COOKIE'] = decode req.params["session_encoded"] unless req.params["session_encoded"].nil?
    end
    @app.call(env)
  end

  private

  def decode(s)
    s.split("x").map{|x| x.to_i.chr}.join
  end
end

Rails.configuration.middleware.insert_before(ActionDispatch::Session::CookieStore, FlashSessionCookieMiddleware, Rails.configuration.secret_token)

Put this file into

config/initializers/flash_session_cookie_middleware.rb

to make sure it’s loaded at startup. With this code we tell Rails to use our own Rack middleware, which – if the request is coming from a Flash client – will rescue our session, otherwise we just call our Rails stack as always. Wondering what this decode method is for? It deals with the problem of Flash that it swallows some chars like ‘+’, so we have to encode our session before to make it Flash-save. As you’ll see, I do this in a very low level way with Ruby because JavaScripts encodeURI() doesn’t escape all dangerous (Flash-) chars.

The Uploadify part

Here comes a little example snippet for your view.

<input type="file" name="file_input" id="file_input" />
<script type="text/javascript">
  $(function() {  
    // encode the session into a Flash-save format
    <% arr = [] %>
    <% request.env['HTTP_COOKIE'].each_char{|c| arr.push(c[0].to_s)} %>
    <% session = arr.join("x") %>
   
    $('#file_input').uploadify ({
      script          : '<%= band_container_items_path %>',
      uploader        : '/flash/uploadify.swf',
      multi           : true,
      auto            : true,
      scriptData      : {'session_encoded': '<%= session %>', 'foo': 'bar' },
      cancelImg       : '/images/cancel.png'  //take care that the image is accessible
    });
  });
</script>

We translate the session in our own format – one that Flash 100% understands. Every char is translated to its ASCII code and connected with a “x” – so we send a string like “104x101x108x108x111x32x119x111x114x108x100″ through Flash – and that is ok.

Hope this post can get you on the right way to get your code up und running.