Politics, Programming and Possibilities
9 Dec
SWFUpload is an open-source Flash 8 embedded application that lets users upload multiple files in a web application. It looked like an easy answer to all of our single-file-uploading woes until we discovered that we couldn’t maintain session state with it.
The problem is that Flash 8 has no way of sending meta data with the uploaded file–for example, who the file belongs to! This is extremely important for us in our iMemoryBook application, since we want uploaded files to be associated with the user’s account–not just a public file repository.
So the culprit (inasmuch as we can’t fix Flash 8 ) appears to be in Ruby’s CGI::Session class: no matter what we tried, it would not pick up the _session_id in the query string when the HTTP request was a POST.
Normally the work-around is quite simple. In a POST-type request, a form is submitted to the server (e.g. a Rails application). In this scenario, we could simply send the _session_id as a hidden field and all would be well.
With Flash 8, however, there is no way to add a ‘hidden field’ to the multi-part form data, thus Rails will fail to recognize the _session_id in the query string portion of our request.
Tracking things down was the hard part. The hackish work-around wasn’t so bad:
# The following code is a work-around for the
# Flash 8 bug that prevents our multiple file uploader
# from sending the _session_id. Here, we hack the
# Session#initialize method and force the session_id
# to load from the query string via the request uri.
# (Tested on Lighttpd)
class CGI::Session
alias original_initialize initialize
def initialize(request, option = {})
session_key = option['session_key'] || '_session_id'
option['session_id'] =
request.env_table["REQUEST_URI"][0..-1].
scan(/#{session_key}=(.*?)(&.*?)*$/).
flatten.first
original_initialize(request, option)
end
end
Put the above code in your environment.rb file, or separate it out as a file in your lib directory and include it.
This code is also available at my favorite snippet place, BigBold’s snippets directory.
Update: The above code doesn’t work on Mongrel/Apache, so I’ve modified it and uploaded the new code here.
30 Responses for "Getting the _session_id from SWFUpload"
[...] If you want to know more about the technical side of this uploader, it’s called SWFUpload. Read Duane’s blog post on it. He is an iMemoryBook programmer and as far as I can tell, a super-genius. You can also check out the deconcept blog. Share This [...]
i’ve got the same problem. but i don’t work with RoR - I work with cakePHP …
do you have a hint how a your workaround wound work in PHP?
Hi, i tried to do what you post and nothing came out of it.
Here is exception i have now.
Can you help ?
stack level too deep
/var/www/pptestsite.com/appsite/current/public/../config/environment.rb:61:in `original_initialize’
/var/www/pptestsite.com/appsite/current/public/../config/environment.rb:62:in `original_initialize’
/var/www/pptestsite.com/appsite/current/public/../config/../lib/session_get.rb:9:in `initialize’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/cgi_process.rb:112:in `new’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/cgi_process.rb:112:in `session’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/cgi_process.rb:142:in `stale_session_check!’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/cgi_process.rb:108:in `session’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:918:in `assign_shortcuts_without_flash’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/flash.rb:141:in `assign_shortcuts’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/base.rb:402:in `process_without_filters’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/filters.rb:377:in `process_without_session_management_support’
/usr/lib/ruby/gems/1.8/gems/actionpack-1.12.5/lib/action_controller/session_management.rb:117:in `process’
/var/www/pptestsite.com/appsite/current/public/../config/../vendor/rails/railties/lib/dispatcher.rb:38:in `dispatch’
/var/www/pptestsite.com/appsite/current/public/../config/../vendor/rails/railties/lib/fcgi_handler.rb:150:in `process_request’
/var/www/pptestsite.com/appsite/current/public/../config/../vendor/rails/railties/lib/fcgi_handler.rb:54:in `process!’
/usr/lib/ruby/gems/1.8/gems/fcgi-0.8.7/lib/fcgi.rb:612:in `each_cgi’
/usr/lib/ruby/gems/1.8/gems/fcgi-0.8.7/lib/fcgi.rb:609:in `each’
/usr/lib/ruby/gems/1.8/gems/fcgi-0.8.7/lib/fcgi.rb:609:in `each_cgi’
/var/www/pptestsite.com/appsite/current/public/../config/../vendor/rails/railties/lib/fcgi_handler.rb:53:in `process!’
/var/www/pptestsite.com/appsite/current/public/../config/../vendor/rails/railties/lib/fcgi_handler.rb:23:in `process!’
/var/www/pptestsite.com/appsite/current/public/dispatch.fcgi:24
The problem was in location in environment.rb
I added it at the very end and its cause an exception above. I spended a lot of time in trying figure out where is the problem then trying to find different place for the class in environment.rb. I tried to specify the reference every time upper from the previous place and found one where its working right. I’m sorry my description may cause laugh but i’m new in RoR and think that its may be helpful for others.
# Uncomment below to force Rails into production mode when
# you don’t control web/app server and can’t set it the proper way
ENV['RAILS_ENV'] ||= ‘production’
# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = ‘1.1.6′
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), ‘boot’)
Rails::Initializer.run do |config|
config.active_record.default_timezone = :utc
end
# Include your application configuration below
require File.dirname(__FILE__) + ‘/../lib/inheritedonly’
class ActiveRecord::Base
include InheritedOnly
end
require File.dirname(__FILE__) + ‘/../lib/session_get.rb’
# Load application configuration
require ‘ostruct’
require ‘yaml’
config = YAML.load_file(”#{RAILS_ROOT}/config/config.yml”)
env_config = config[RAILS_ENV]
config['common'].update(env_config) unless env_config.nil?
AppConfig = OpenStruct.new(config['common'])
In this location its working fine but if you move the reference to session_get.rb (where the script for fix uploader located) under load application configuration section it cause the exception i wrote.
Worked great! Brilliant! Thank-you!
I feel a little ignorant asking this, but I don’t know what to do with the session id once I have it. Is there a way to find and load the associated rails session?
Jared: Rails loads the session data automatically for you (or should!) It’s all available in the “session” hash. For example: session[:user_id] might be how you would access your app’s user id.
ok, i just didn’t understand that the provided code was also loading the session.
The code doesn’t seem to be working for me though (I’m using Mongrel 1.0.1)
Here’s what my swfobject code looks like:
mmSWFUpload.init({
upload_backend : “/pages/createFromFlash?album_id=1&_session_id=9dd31a3a6cfbd2eaa78694f0d38728ed”,
target : “SWFUpload”,
//link_mode : “standalone”,
//link_text : “Upload”,
allowed_filesize : “400000″,
allowed_filetypes : “*.jpg;”,
upload_start_callback : ‘uploadStart’,
upload_progress_callback : ‘uploadProgress’,
upload_complete_callback : ‘uploadComplete’,
upload_error_callback : ‘uploadError’,
upload_cancel_callback : ‘uploadCancel’,
upload_queue_complete_callback : ‘uploadQueueComplete’
});
so when I upload the file i hit the createFromFlash action, and the session id doesn’t match up:
Processing PagesController#createFromFlash (for 127.0.0.1 at 2007-03-12 13:29:27) [POST]
Session ID: a4662800ed6bd130e5d7665b251f0c90
am I doing something wrong? Has anyone else gotten this working on mongrel 1.0.1?
It is possible to upload a file with more data using XHR (XmlHttpRequest) - also called AJAX.
The trick : you upload the file with SWFupload, tag it on the server so as you can find it in the future.
When the upload is done, you send all your information with XHR. This information must contain the tag you gave the file on the server.
That’s it.
NB. I use md5 to tag the file.
an update for anyone who has the same problem as i did, I added some debugging code to the provided code and found that the session id was not in the query string once it got to the initialize method. When I removed the other parameter I was trying to pass as well, the session id showed up, but I still haven’t been able to get them both to show up in the query string. I’ve tried using both & and & in the upload backed string to seperate the vars in case that mattered, but neither seems to work. Maybe this is a problem with the version of swfupload I’m using (0.8.6).
(the second & up there was supposed to be the html entity & amp;)
i had a little set back due to the fact that this post is not completely flayed out. what it’s missing is the explicit detail that you have to dynamically append the query string with the current session id in the SWFUpload declaration.
i.e.
upload_script : “/upload?_session_id=”
now most rails apps session key is not _session_id it’s RAILS_APP_NAME_session_id and can be found in application.rb so it would use that key in the upload_script parameter.
and having rails not render anything (i.e. render :nothing => true) helps clear up those pesky http 201 errors.
[...] Going Up » Getting the _session_id from SWFUpload (tags: swfupload session) [...]
[...] If you need sessions for your uploads (user needs to be logged in to upload), this blog post will come in very handy: http://blog.inquirylabs.com/2006/12/09/getting-the-_session_id-from-swfupload/ Links, Ruby Home | | Login|Feed © 2007 GSIY … Ruby-Rails Portal | GooMedic.com | Neoxero.com [...]
I don’t think it’s possible to pass 2 param after the php script…
I have the same problem, I think I will separate them “manually” (with a separator I’ll choose).
[...]upload.php?param=4|otherparam[...]
for instance
This won’t work in Rails 2.0.2. This approach yields a SessionFixationAttempt to be thrown, or am I doing something wrong?
!\ FAILSAFE /!\ Thu Jan 10 02:29:32 -0500 2008
Status: 500 Internal Server Error
ActionController::CgiRequest::SessionFixationAttempt
/Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/cgi_process.rb:116:in `session’
/Library/Ruby/Gems/1.8/gems/actionpack-2.0.2/lib/action_controller/cgi_process.rb:166:in `stale_session_check!’
[snip]
Hi Josh,
Rails 2 has a new feature that prevents the session id from being changed in the GET string. There is a setting that will make it work again, however. In your ApplicationController (or a more specific controller) add the following:
session :cookie_only => false
This will allow sessions in the manner we need. It is a slight security risk, however, which is probably why the Rails guys made the SessionFixationAttempt error a default.
Hi
I tryed a lot of things with success to make it works under rails 2. I do not even have a SessionFixationAttempt. Just my before_filter :login_required does not work as session does not seems to correct.
Did I miss something, any chance to share a sample app?
Thanks a lot
Seb
I used the following approach in Rails 2
1. Disable CookieStore for sessions
in inviroment.rb
config.action_controller.session_store = :p_store
2. add the following modified code to the bottom of enviroment.rb
class CGI::Session
alias original_initialize initialize
def initialize(request, option = {})
if option['swfupload'] == true
tmp = request.env_table["REQUEST_URI"][0..-1].match(/[?&]swfupload_sid=(.+)(&|$)/)
if tmp and tmp[1]
option['session_id'] = tmp[1]
end
end
original_initialize(request, option)
end
end
3. Add “session :swfupload => true” to your controller
class Admin::UploadController true
….
end
Agter this your controller will tale session id from “swfupload_sid” GET variable
I cannot switch to :p_store, I have to work with cookie sessions :(.
This is a life saver!
Thanks!
[...] [...]
I’m still a little confused as to how to get this to work in Rails 2 (maybe I’m tired) I’m gonna have another look when I’m fresher in the morning.
Hi
I have tried out dave south´s version (SWFUpload, attachment_fu and mime_fu), since i am a total newbie on ROR, i am absoluty thrilled, and understand only parts.
I should says that i have put in the above code snippet i environment.rb, while running the app i can view the log an d see that there is a session id produced ( i have created a column for storing the session id, still says NULL, i.e. session id does not get stored into the database), shoudn´t there be something in the models/photo.rb (in my case) that tells the database, here comes the session id.
Sinc Kalle
[...] to Inquiry Labs for sharing the above [...]
Ok… i got the session ID to work sweet as… now i need to be able to add things like the Album_id as a params… i don’t want to have to use a hidden field. can anyone help? cheers
Coops
SWFUpload is awful !
Ive had more bugs with this uploader than any code I have ever worked on. I highly recommend you look at other stuff.
Very poorly documented.
[...] SWFUpload and Rails work together Getting the session_id from SWFUpload Uploading files with [...]
I’ve put together a sample Rails 2.1 app showing SWFUpload integrated with attachment_fu and restful-authentication if anyone’s interested. It’s up on Github - details here - http://cameronyule.com/2008/07/rails-21-swfupload-example
[...] gist of it is that Flash cannot send cookie data so if you’re using a cookie based authentication mechanism, it will fail. The solution is to [...]
Leave a reply