There are a ton of possible upload progress solutions for Web applications that need them. I had been using the upload progress that came with Merb, but I had modify mup.js to make Ajax calls using mootools instead of Prototype. Ultimately the progress status wasn’t updating consistently, so I sought a pure mootools compatible solution.
That brought me to the FancyUpload upload progress solution by Harald Kirschner. It’s Adobe Flash based, meaning upload status can be determined on the client side using Flash itself. It’s easy to use and fairly sexy, but Flash comes with its own bundle of deep hurting.
I discovered that Flash 8/Win does something extremely retarded. It sends an initial POST with a Content-Length of 0. Sane multipart MIME parsers simply toss it out as the garbage it is. Unfortunately, that means Rails via CGI.rb and Merb are raising exceptions and Flash is giving up. I don’t agree with the solution to directly monkey patch CGI.rb; I don’t know enough about multipart MIME parsing to feel secure running a monkey patched CGI.rb on a production system.
Moreover, I am using Merb, and its multipart MIME parser is completely different than CGI.rb, meaning I’d have to come up with my own monkey patch without potentially compromising security. No, thanks.
Instead, I am taking advantage of Apache 2.2, which I am already using for mod_proxy_balancer between several Mongrel instances, to fix Flash 8’s broken HTTP POST. If you’ve ever wondered how to do some extremely strange stuff using Apache’s powerful rewrite rules, examine my solution to Flash 8’s suckiness below.
BrowserMatch ^Shockwave Flash$ swf=true
RewriteCond %{HTTP:Content-Length} ^0$
RewriteCond %{ENV:swf} ^true$
RewriteRule . %{REQUEST_URI}?flash8win=1 [QSA]
RewriteCond %{QUERY_STRING} flash8win
RewriteRule . - [E=strip_content_type:true]
RequestHeader unset Content-Type env=strip_content_type
First, we check to see if it’s a Flash client. Flash 8 wasn’t reporting a version string in any of my packet captures, so I simply used the string ‘Shockwave Flash’. Apache’s BrowserMatch allows you to set an environment variable which I do if the client is Flash.
Next, I perform a series of checks to see if the current request is a broken one created by Flash 8/Win. If the Content-Length is 0, we have a winner. The query string is overloaded with a string signaling Flash 8/Win has been detected.
Finally, I perform a match and if my query string indicates I found Flash 8/Win, I set an environment variable which RequestHeader can match upon. The Content-Type header is then removed. Without a Content-Type header indicating a multipart POST, the MIME parser is skipped and CGI.rb and Merb’s parser have nothing to do and I can ignore the empty request entirely.
def handle_upload
if ! params[:flash8win].blank?
render :text => '', :status => 200
return
end
.. other code ..
end
Clearly, you can do some amazing things with Apache2 and its rewrite rules.
It’s useful to note that under Merb 0.3.4, a 500 error is returned when the MIME parser encounters something worthy of raising an exception. Flash 8/Win doesn’t care if it gets a 500 response to its initial Content-Length: 0 request. It did care when it received a 404 Not Found, which is what Merb 0.2.0 and Mongrel using CGI.rb return. If I simply upgraded to Merb 0.3.4, I would never have written any rewrites rules, but had tons of mysterious 500 errors in my merb.4000.log.
Yay Flash.
6 Comments
Sweet! I remember your name from some linux list. (jaxlug or one of the other local lists near Alachua County).
I’m not much of a mootools fan. jQuery!!!! Regardless, this is cool!
Nice work, I also use merb in some of my new projects and I’m sure this will help and I will also add that to the doku of the upcoming Swiff.Uploader class in MooTools 1.2.
Jim: For small small things I tried also jq, but for professional Javascript apps/backends MooTools is the best choice since it has so many helpers, bringing back the fun to javascript ;)
I never had problems with the empty header in my other projects since they depended on a proper validation and since this empty request has no real data they never reached the controller.
Can you think of any other way to implement this idea without .htaccess?
The unset rule throws a 500 error for my apache config.
I only get the issue of the first file not being posted on my linux system where I’m running a “non-free” version of flash (gutsy gibbon).
Any advice? I’ve been battling this one all week.
Another way of implementing this? I spent about six hours figuring that way out. If you want to try something else, you’re on your own.
What error? Did you enable all the necessary modules?
As to the userland flash, under Linux it seems to have issues. Firefox simply crashes upon a successful upload. I couldn’t get Opera to even care. I didn’t try messing with Konqueror. One of the few drawbacks to flash uploading, for me anyway, is that I can’t test it under GNU/Linux where I do most of my development.
You’d have to email me with specific messages for me to be able to attempt to help.
Yeah, sorry. I didn’t really mean another way to implement this. I’ve kinda given up on the issue. For me in Linux nothing crashes but the first file is never submitted. It sounds to be relative to this content length concept, but I wouldn’t know. As long as it works in the windows environments I should be fine.
Thanks for your great post and those 6 hours of dedication to uncover it. I spent about the same and then gave up :)
I see the Flash 8 version comments on this everywhere, but it seems like this is still happening on Flash 9. Can anyone confirm if this is still in Flash 9?