Rails can't handle binary file upload?

Hi,

I am trying to upload local file to a rails-app through HTTP without multipart encoding. Doing so, I expect to get some performance boost on transferring large files. I am stucked Rails can't handle binary file upload, while it's OK with plain text files. Let me show a sample application skeleton:

config/routes.rb:

map.connect 'upload', :controller => 'application', :action => 'upload'

app/controllers/application.rb:

class ApplicationController < ActionController::Base def upload    render :text => request.raw_post end end

Uploading plain file succeeds as following:

$ curl -v -T 42.txt http://localhost/upload * About to connect() to localhost port 80 * Trying 127.0.0.1... * connected * Connected to localhost (127.0.0.1) port 80

PUT /upload HTTP/1.1

User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7i zlib/1.2.3 Host: localhost Pragma: no-cache Accept: */* Content-Length: 3 Expect: 100-continue

< HTTP/1.1 100 Continue < HTTP/1.1 200 OK < Date: Mon, 25 Sep 2006 18:51:11 GMT < Server: Mongrel 0.3.13.4 * Added cookie _session_id="b64a0dde1c857bd0d94ea95869f90294" for domain localhost, path /, expire 0 < Set-Cookie: _session_id=b64a0dde1c857bd0d94ea95869f90294; domain=localhost; path=/ < Status: 200 OK < Cache-Control: no-cache < Content-Type: text/html; charset=UTF-8 < Content-Length: 3 42 * Connection #0 to host localhost left intact * Closing connection #0

Uploading binary file fails like this:

$ curl -v -T photo1.jpg http://localhost/upload * About to connect() to localhost port 80 * Trying 127.0.0.1... * connected * Connected to localhost (127.0.0.1) port 80

PUT /upload HTTP/1.1

User-Agent: curl/7.13.1 (powerpc-apple-darwin8.0) libcurl/7.13.1 OpenSSL/0.9.7i zlib/1.2.3 Host: localhost Pragma: no-cache Accept: */* Content-Length: 60591 Expect: 100-continue

< HTTP/1.1 100 Continue < HTTP/1.1 200 OK < Date: Mon, 25 Sep 2006 18:52:15 GMT < Content-Type: text/html < Content-Length: 380 Status: 500 Internal Server Error Content-Type: text/html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"   "http://www.w3.org/TR/html4/loose.dtd&quot;&gt; <html> <body> <h1>Application error (Apache)</h1> <p>Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html</p> </body> * Connection #0 to host localhost left intact * Closing connection #0 </html>

I've written small cgi application to prove it might perform better, and it doesn't show any problem. So I would like to apply my idea to Rails application too. Comments are appreciated.

Rails probably can't, but you could go straight to source and use a Mongrel handler. Here's the shell to get you started:

class BinaryUploadHandler < Mongrel::HttpHandler

  def process(req, resp)     # req.body at this point is a IO object, either StringIO or Tmpfile   end end

uri "/uploadstuff", :handler => BinaryUploadHandler.new

Put that in a mongrel.conf and start mongrel with:

mongrel_rails start -e production -S mongrel.conf

Then you're job is to fill in the process(req,resp) method to take the req.body and either move it or copy it to where you want. You also have access to headers and other goodies and can do responses.

Check out the code to mongrel_upload_progress (gem install mongrel_upload_progress) for a good example of doing this and also tracking the upload. You could probably just modify that gem and be done with 80% of the work already.

Hi, Zed.

I had already implemented with standalone mongrel-powered server which rely on a number of existing rails models. However, I'd like to handle file-upload on my rails-app, because well... here is rails list!? :slight_smile:

Another question: mongrel can handle 100 Expect request? FYI, here's my sample CGI application:

if ENV['HTTP_EXPECT'] == '100-Continue'   puts "Status: HTTP 100 Continue"   content = STDIN.read(ENV['CONTENT_LENGTH'].to_i)   File.open('upload.data', 'w').write(content) end puts "Content-Type: text/html" puts puts "Done."