20

I can't seem to find a simple and clear answer to this problem anywhere! Everything seems either outdated or incomplete!

I just want the user to be able to click on a link or button and download a file (that is somewhere in the public folder)

I tried this:

#view
<%= link_to "Raw blast output" ,:action => :download, :file_name => "public/data/02_blastout/#{@bl_file}" %>
#controller
def download
    send_file "#{RAILS_ROOT}/#{params[:file_name]}"
end

but I get this error:

No route matches {:action=>"download", :file_name=>"public/data/02_blastout/input0.fa_x_Glyma1aaunq.bl", :controller=>"cvits"}

Thanks for the help!!

Richard
  • 56,349
  • 34
  • 180
  • 251
bdeonovic
  • 4,130
  • 7
  • 40
  • 70
  • 1
    This seems like a terribly unsafe way to handle this, especially considering you can just link directly to the file. – Mario Jun 17 '11 at 21:29
  • 2
    Someone could edit your link like this: `?file_name=config/database.yml` – Mario Jun 17 '11 at 21:31
  • I would be more than happy to hear of the "correct way" for a user to obtain a file!!! Open to any suggestions! – bdeonovic Jun 17 '11 at 21:32
  • Well if the file is in public, than you can just give them a link to it. Anything in public is available under the root of the site. Something like `/public/thing.png` is available as `/thing.png`. – Mario Jun 17 '11 at 21:34
  • 1
    send_file should never be given a path set by a param, this opens up a major security hole that malicious users will exploit. – Mario Jun 17 '11 at 21:36
  • Thanks thats exactly what I want! Sorry, still pretty new to web development and rails in particular. :) stackoverflow has such a better response time than any other forum! – bdeonovic Jun 17 '11 at 21:37
  • may be this http://ap.rubyonrails.org/classes/ActionController/Streaming.html ? – AndrewShmig Jun 17 '11 at 21:28
  • @Andrew - use what? I am already using the send_file method. – bdeonovic Jun 17 '11 at 21:29

1 Answers1

40

Don't use send_file with a parameter set by a user. This opens up a massive security hole, allowing a user to access any file that is readable by your application (namely, your entire application, but also possibly other files on the filesystem).

Rather, if the file is under public, link to the file itself. In your case:

<%= link_to "Raw blast output", "/data/02_blastout/#{@bl_file}" %>

No need for a special controller action.

Mario
  • 2,942
  • 1
  • 25
  • 38
  • 2
    Won't the download lockup the rails process? Shouldn't the download instead be handled by the http server (apache, etc.) as in: http://www.therailsway.com/2009/2/22/file-downloads-done-right/ – Josh M. Sep 29 '13 at 02:28
  • 2
    If you set up apache or another server, it should handle that. This is just the code for the link. – Mario Sep 30 '13 at 03:36
  • 1
    What if it is not a public file?, and it's only for registered user's? – miguelfg Aug 25 '15 at 12:15
  • Mario makes a good point. But one could check if the file is within a specific directory that is expected to be a directory for downloadable content, or if the file is registered in a table of downloadable files. No? – Julien Lamarche Oct 29 '21 at 14:00
  • https://web.archive.org/web/20160201222918/http://www.therailsway.com/2009/2/22/file-downloads-done-right/ archived link mentioned by @JoshM. – Tun Jul 20 '22 at 05:17