Uploading Files to Rails Metal

I was fooling around with Rails Metal earlier today to get a better feel for it. One of the most obvious uses for me is disassociating an upload task from your application; a single Rails instance blocks while waiting for an upload to finish, so particularly for very large file uploads, your application can stop responding to users if all instances are taken up by uploaders.

With Metal, you can spawn separate tasks on different process IDs: then when you need to upload, Rails calls to them and that Metal handles the upload (and blocks), while the application instance itself is free to service other requests. This article was super helpful in describing how to use Rackup to start a Metal piece as a separate process. But in all my searching I didn't find anything clearly delineated on the Internet for how to make Metal work with file uploads, so this is how I did it.

This code is set up to assume that you immediately want to do something with the file upload with a model. If you want to just save the file on the filesystem, you can do that instead too.

The metal file, Uploader.rb, looks like this:

class Uploader
  def self.call(env)
    if env["PATH_INFO"] =~ /^\/upload$/
      request = Rack::Request.new(env)
      uploaded_file = request.POST["uploaded_file"][:tempfile].read
      @report = Report.create
      @report.attach_file(uploaded_file)
      [302, {"Content-Type" => "text/html", "Location" => "/reports/#{@report.id}"}, ["<html>You are being <a href='/reports/#{@report.id}'>redirected.</a></html>"]]
    else
      [404, {"Content-Type" => "text/html"}, ["Not Found"]]
    end
  end
end

And the corresponding view is quite simple:

    <% form_tag 'upload', :multipart => true do %>
        <label>Select a file to upload:</label>
        <%= file_field_tag 'uploaded_file' %>
        <%= submit_tag "Upload" %>
    <% end %>

Obviously the magic is in the request.POST["uploaded_file"][:tempfile].read bit. The file is saved by the webserver in a temporary location, which you can access with [:tempfile].read. Just make sure to save it to a more stable place if you intend to keep it.

Related Services: Ruby on Rails Development, Custom Software Development

Related posts:

  1. Combine Multiple CSS/JS Files Into a Single File in Rails
  2. Review: Head First Rails
  3. Ruby Internals: Overriding Object.require() to watch files as they are loaded
  4. Rails Test Prescriptions is now on sale
  5. git how to: ignoring files in git

Comments: 3 so far

  1. I may be wrong, but I believe that both mongrel and phusion passenger buffer file uploads and don’t hand them off to rails until the upload is complete. This means that a rails instance is not blocked waiting for the upload to finish. However once the upload is complete and rails starts processing the request, you may still want to use metal if you are doing any expensive processing on the uploaded file.

    Comment by Jared, Wednesday, March 4, 2009 @ 9:54 am

  2. Check out this thread for a little more detail around what Jared is talking about:

    http://groups.google.com/group/phusion-passenger/browse_thread/thread/f986a14609abf4a6

    Comment by Trevor Turk, Wednesday, March 4, 2009 @ 8:38 pm

  3. I guess the code related to the view is missing… there’s only a label there!

    Comment by Nando Vieira, Thursday, March 5, 2009 @ 4:44 pm

Leave a comment

Powered by WP Hashcash

Launch: Pathfinder Newsletter

    Get a monthly update on best practices for delivering successful software.

    Subscribe via email


    Subscribe via RSS      RSS icon

Topics

Search

WordPress

Comments about this site: info@pathf.com