I’m attempting to use send_file to send an image file from
public/images. The file is world readable.
I’ve tried supplying the full path, but it still can’t seem to find
file. Here is the code that calls send_file:
I don’t think I would do this on purpose, if the image is really in
public. If you want to change the image name at run time, I would
instead generate an tag in the view, which the browser will then
download (per the setting of the user).
Otherwise, if you wish to send content that is not inside your web
(e.g. in /public or below) then you might do something like:
Force all requests for /protected/whatever to the render_static
Static content control
map.connect ‘/protected/*path’, :controller => ‘protected’,
:action => ‘render_static’
# see routes.rb. A map is made so that the URL is
# visible as params[:path]
requested_file = params[:path].to_s
safe, opf = safe_to_send?(requested_file)
# compute the mime type and send the content here
# This should be made much more concise (a hash lookup)
# and extended to cover more mime types
if requested_file =~ /\.pdf$/ then
apptype = "application/pdf"
elsif requested_file =~ /\.ppt$/ then
apptype = 'application/vnd.ms-powerpoint'
elsif requested_file =~ /\.swf$/ then
apptype = 'application/x-shockwave-flash'
apptype = 'octet/stream'
send_file opf, :type=> apptype, :disposition=> "inline"
# Absolute file location. Beware that funny business with RAILS
# (e.g. symbolic links) could throw this off
output_base = File.expand_path RAILS_ROOT
output_file = File.expand_path output_base + ‘/secret/’ +
# Attempt to foil directory traversal attacks
# make sure that output base is the beginning portion of output_file
# I *think* rails makes this check unnecessary, but hey...
result = false
if output_file.index(output_base) == 0 and File.exists?(output_file)
result = true
return result, output_file
Note that I’m being paranoid about traversal attacks. I believe rails
already deals with all of the . and … stuff when the URL is submitted
by the browser, however, I could be wrong.
In this case, the protected content is in a subdirectory called
‘secret’, which is one level below the main root of the rails app.