I assume that:
- your files are at /mass-storage/protected-files/
- you're starting off with Ezra's nginx configuration
- you want to serve files at http://localhost/files/your/path/here via a FilesController
First, you need something like this in your routes.rb:
map.connect 'files/*path_info', :controller => 'files', :action => 'index'
This will send the extra path stuff into a path_info variable without any additional parsing.
Next, you'll want to add this to your nginx.conf after your "location /" block:
location ^~ /protected-files/ { root /mass-storage internal; }
This registers /protect-files urls which will serve from /mass-storage/protected-files, but can only be accessed via the magic X-Accel-Redirect header that nginx understands.
A minimal FilesController might look like this:
class FilesController < ApplicationController def index # insert any permissions check here path = params[:path_info] response.headers["Content-Type"] = "application/octet-stream" response.headers["Content-Disposition"] = "attachment; filename=\"#{File.basename(path)}\"" response.headers["X-Accel-Redirect"] = "/protected-files/#{path}" response.headers["Content-Length"] = File.size("/mass-storage/protected-files/#{path}") response.headers["Status"] = "200 OK" response.body = "" @performed_render = true end end
I'm setting @performed_render and response.body by hand because if you
render :nothing
in Rails 1.1.6, it clobbers your Content-Length. Nginx does not touch any of the headers I set. Later versions of Rails fixed this, so you should render :nothing
in newer versions!You might want your file path to be
/mass-storage/files
and your internal url to be /files
to match what people are typing into their browser, but you must resist! When I tried this, nginx seemed to get confused and would just raise a 500 because it thought it was stuck in a redirection loop.