Value driven web development

As you know Rails does bad on handling file upload, a large file will block your Rails app a long while, make it busy on receiving the file and can’t give response to other visitors, make them upset and leave you alone.

One solution is using merb to handle file upload for rails. The latest Merb that build on Rack(a cool framework who help you dealing with all kinds of http servers) does a really good job on uploading.

First, install merb:


sudo gem install merb

Second, create a merb app in your rails dir:

1
2
    merb-gen app uploader
    cd uploader

You can ignore all other files except config/rack.rb, this is the only file we need to modify. Currently there’s only one line in the file:


    run Merb::Rack::Application.new

It will ask merb to handle the http request come from rack. Let us change this file to:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
require 'cgi'

class File
  def to_s
    path
  end
end

# build a new handler to handler rack's request
  class Uploader 
    def call(env)
      # leverage merb's utility to parse the request.
      # Merb will save the file to a tempfile and save the tempfile's path in request's param
      request = Merb::Request.new(env)

      params = request.params

      # pass the params directly to the real (rails) app
      result = post("http://someplace.com/api", hash_to_params(params)).split("\n")[-1]
     
      # processing result or just ignore it ...
    end

    private

    def post(url, params="")
      curl_cmd = "curl -H \"Content-type: application/x-www-form-urlencoded\" #{url} -d \"#{params}\""
      puts "curl_cmd = #{curl_cmd}"
      f = IO.popen(curl_cmd +" 2>&1")
      result = f.read
      f.close
      result
    end

    def hash_to_params(hash)
      hash.map do |k, v|
        if v.kind_of? Hash
          h = {}
          v.each { |kk, vv| h["#{k}[#{kk}]"] = vv }
          hash_to_params h
        else
          "#{k}=#{CGI.escape v.to_s}"
        end
      end.join("&")
    end

  end

run Uploader.new
Run merb by
merb -p 1234 -c 1 -e production -d

Remember to config your apache or your favorite webserver to redirect all request from /uploader to port 1234 (Your merb uploader is listening here!).

Pretty easy, isn’t it?

2 Responses to “Uploading large files to Rails with Merb”

  1. IceskYsl Says:
    只是把上传请求接收后再转给Rails进程处理? 那么如果因为上传大文件处理比较慢,导致进程数被占用,这样的方法还是无法解决了?
  2. Rex Says:
    If your doing long file manipulation process, it should be passed on to a background process. We currently use a messaging system + distributed workers in Ankoder.

Sorry, comments are closed for this article.