Thanks Ben,

One more question, How does this work in a dev environment without
unicorn / nginx ?
I'm just running the default "rails s" WEBrick server on dev.

I'm guessing the answer will be "dev & production should run an
identical stack" so you can test / debug these things before deploy.

Cheers,

On Nov 4, 9:22 am, Ben Hoskings <[email protected]> wrote:
> You should move the files outside of the public/ directory, into something 
> like data/ or assets/ at the top level of your app, and serve them through 
> authenticated requests with nginx.
>
> The way it works is that instead of rails sending the data itself, it adds a 
> header to the response containing the filesystem path to the file. The header 
> is one that nginx recognises, and it inserts the file data into the response.
>
> This is far better than serving the file with rails because serving static 
> content is what nginx is built to do, extremely fast; doing it with rails is 
> really inefficient because you have to load the contents of the file into 
> ruby land. It's slow and your rails process' resident size will balloon. When 
> nginx serves the content, however, it streams the file from disk in the 
> fastest way possible on your OS, using a fixed amount of memory no matter how 
> large the file is.
>
> You serve them from an internal location in nginx. This is a location that 
> isn't accessible publicly, but that nginx will serve from if asked to by the 
> upstream. Add something like this to your vhost config (replace /assets with 
> the path within your app (outside public/) that you chose for the images):
>
> location /assets {
>   internal;
>   root /srv/http/your.app/current; # don't include public/
>
> }
>
> Then, to serve a file within the action on one of your routes, you just set 
> the right headers for nginx, and render an empty response:
>
>   def photo
>     # The model and paths are totally made up cause I haven't seen your app :)
>     photo = Photo.find(params[:id])
>     x_accel_redirect "/assets/photos/#{photo.id}/#{photo.style}.jpg",
>       :disposition => 'attachment',
>       :filename => photo.filename,
>       :type => photo.content_type
>   end
>
>   private
>
>   # Maybe put this private method in application_controller or
>   # in a module if you want to use it in a few different places.
>   def x_accel_redirect path, opts
>     send_file_headers!({
>       :disposition => 'attachment'
>     }.merge(opts))
>     response.headers['X-Accel-Redirect'] = path
>     render :nothing => true
>   end
>
> There's a way to do this by instructing rails to, using 
> "config.action_dispatch.x_sendfile_header", but I can't remember it offhand. 
> I prefer to just explicitly set the headers anyway, it's easy and there are 
> no shenanigans that way.
>
> - Ben
>
> On 03/11/2011, at 10:28 PM, markbrown4 wrote:
>
>
>
>
>
>
>
> > Yes, the routes were setup so photo's & documents are only accessible
> > to logged in users.
> > The files are in a /private/ directory.
>
> > I'm new to nginx and rails so I need to take baby steps through this
> > one.
> > If I delete the routes how else can I authenticate and serve the
> > images with the headers?
>
> > Here's the current config
> > --
> > server_name <%= var :domain %> <%= var :extra_domains, :default => ''
> > %> <%= var :www_aliases %>;
> > root <%= var(:app_root) / 'public' %>;
>
> > if ($host !~ ^<%= var(:domain).gsub('.', '\.') %>$) {
> >  rewrite ^(.*)$ http://<%= var(:domain) %>$1 permanent;
> > }
>
> > location ~* \.(js|css|jpe?g|png|gif|ico|eot|woff|ttf|swf)$ {
> >  if ($request_uri ~ "\?[0-9]+$") {
> >    expires max; # cache timestamped assets forever...
> >    break;
> >  }
> >  if ($request_uri !~ "\?[0-9]+$") {
> >    expires 1d; # ...and others for 1 day
> >    break;
> >  }
> > }
>
> > error_page 503 /system/maintenance.html;
> > location /system/maintenance.html { return 503; }
>
> > try_files /system/maintenance.html $uri/index.html $uri.html $uri
> > @app;
>
> > location @app {
> >  proxy_pass            http://<%= upstream_name %>;
> >  proxy_redirect        off;
>
> >  proxy_buffer_size     64k;
> >  proxy_buffers         32 16k;
> >  client_max_body_size  128m;
>
> >  proxy_set_header      Host            $host;
> >  proxy_set_header      X-Real-IP       $remote_addr;
> >  proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
> > }
> > --
>
> > Here's an example of the current action.
>
> > class ResourceController < ActionController::Base
>
> >  def resume
> >    head(:not_found) and return if (user =
> > Person.find_by_id(params[:id])).nil?
> >    head(:forbidden) and return unless user.downloadable?
> > (current_person)
>
> >    path = user.resume.path(params[:style])
>
> >    if File.exist? path
> >      send_file(path, :type => user.resume.content_type)
> >    end
> >  end
>
> > end
>
> > --
>
> > Thanks,
>
> > On Nov 3, 5:48 pm, Ben Hoskings <[email protected]> wrote:
> >> Do you need to serve those assets through the stack? Serving them with 
> >> nginx will be orders of magnitude more efficient (i.e. webscale).
>
> >> Anything that would otherwise 404 will be passed to your app. If nginx is 
> >> serving them then they're present on disk, so I'd delete the routes and 
> >> let nginx take care of it :)
>
> >> If you want authentication in front of those routes, then you should move 
> >> them outside public/, and use the 'X-Accel-Redirect' header to serve them 
> >> with nginx on authenticated requests.
>
> >> - Ben
>
> >> On 03/11/2011, at 11:02 AM, markbrown4 wrote:
>
> >>> Hi,
>
> >>> We have routes setup for downloading images / documents
>
> >>> get  'photo/:id/:style.:format',  :controller => 'resource', :action
> >>> => 'photo'
> >>> get  'photos/thumb/missing.png',  :controller => 'resource', :action
> >>> => 'photo_missing'
> >>> get  'resume/:id/:style.:format', :controller => 'resource', :action
> >>> => 'resume'
> >>> get  'file/:id/:style.:format',   :controller => 'resource', :action
> >>> => 'file'
>
> >>> Nginx config is skipping the routing though.
>
> >>> Do I need to prevent all images from being served directly to resolve
> >>> it or is there a better way?
> >>> location ~* \.(js|css|jpe?g|png|gif|ico|eot|woff|ttf|swf)$
>
> >>> Thanks,
>
> >>> --
> >>> You received this message because you are subscribed to the Google Groups 
> >>> "Ruby or Rails Oceania" group.
> >>> To post to this group, send email to [email protected].
> >>> To unsubscribe from this group, send email to 
> >>> [email protected].
> >>> For more options, visit this group 
> >>> athttp://groups.google.com/group/rails-oceania?hl=en.
>
> > --
> > You received this message because you are subscribed to the Google Groups 
> > "Ruby or Rails Oceania" group.
> > To post to this group, send email to [email protected].
> > To unsubscribe from this group, send email to 
> > [email protected].
> > For more options, visit this group 
> > athttp://groups.google.com/group/rails-oceania?hl=en.

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
or Rails Oceania" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/rails-oceania?hl=en.

Reply via email to