zimbatm asked:

How do I make an action send a file to the browser ?

Given the controller :

class MyController < Nitro::Controller
  def download
    f = File.open('/etc/passwd')
    # ???
  end
end

What should I put at # ??? to send /etc/passwd to the user ? (high security :-))

(2 attempts)

zimbatm answered:

The following code is a possible solution altrough I would like to avoid putting custome headers. The force-download header have only been tested in Firefox 1.5 under Linux.

Note: I use the streaming capabilities of Nitro to avoid big memory consuption.

require 'nitro/cgi/stream'

class MyController < Nitro::Controller
  def download
    f = File.open('/etc/passwd')
    @context.response_headers["Content-Disposition"] = 'attachment; file="passwd.txt"'
    @context.response_headers["Content-Transfer-Encoding"] = 'binary'
    stream f
  end
end

Rating: 5

Tyche answered:

I am using this. I put in /nitro/cgi/sendfile.rb

require 'nitro/render'

module Nitro

module Render

  # Send a file download to the client.
  #
  # Like render and redirect, the action is exited upon calling
  #
  # [+fname+]  That name of the file
  # [+path+]   Specifying true mean fname contains the full path.
  #     The default, false, uses Server.public_root as the path.
  #
  # [+return+] true on success, false on failure
  #
  # === Examples
  #
  # require 'nitro/cgi/sendfile'
  # class MyController < Nitro:Controller
  #   def download(fname)
  #     sendfile(fname)
  #   end
  # end
  #
  # class MyController < Nitro:Controller
  #   def download()
  #     sendfile('/etc/password', true)
  #   end
  # end
  def sendfile(fname=nil, fullpath=false)
    fname = fullpath ? fname : "#{Server.public_root}/#{fname}"
    f = File.open(fname, "rb")
    @context.response_headers["Cache-control"] = 'private'
    @context.response_headers["Content-Length"] = "#{File.size?(f) || 0}"
    @context.response_headers["Content-Type"] = 'application/force-download'
    @context.out = f
    raise RenderExit
  end

end

end

Rating: 4