Ruby on rails, forcing the user to download a tmp file
-
28-10-2019 - |
Question
I've created a file in the tmp directory with the following controller code:
def download
file_path = "#{RAILS_ROOT}/tmp/downloads/xxx.html"
data = render_to_string( :action => :show, :layout => nil )
File.open(file_path, "w"){|f| f << data }
flash[:notice] = "saved to #{file_path}"
end
This creates the file I wanted in the tmp directory, what I want to do is force the user to download that file.
On my local machine, the file is saved to path like:
/Users/xxxx/Documents/Sites/xxxx/Website/htdocs/tmp/downloads/xxxx.html
And on the live server this url will be somthing totally different.
What I was wondering is how do I force the user to download this xxxx.html ?
P.S. If I put a...
redirect_to file_path
...on the controller it just give's me a route not found.
Cheers.
Solution
Take a look at the send_file method. It'd look something like this:
send_file Rails.root.join('tmp', 'downloads', 'xxxxx.html'), :type => 'text/html', :disposition => 'attachment'
:disposition => 'attachment' will force the browser to download the file instead of rendering it. Set it to 'inline' if you want it to load in the browser. If nginx is in front of your Rails app then you will have to modify your environment config (ie. environments/production.rb):
# For nginx:
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect'
OTHER TIPS
It's easy to confuse file paths with URLs, but it is an important distinction. What has a URL path of /a/b.txt
is actually located in the system path #{Rails.root}/public/a/b.txt
so you may need to address this by generating both in tandem.
Here's how you might address that:
def download
base_path = "downloads/xxx.html"
system_path = File.expand_path("public/#{base_path}", Rails.root)
url_path = "/#{base_path}"
File.open(file_path, "w") do |f|
f.puts render_to_string(:action => :show, :layout => nil)
end
flash[:notice] = "saved to #{base_path}"
redirect_to(url_path)
end
You cannot redirect to a resource that is not exposed through your web server, and generally only things in public/
are set this way. You can include additional paths if you configure your server accordingly.
You can also side-step this whole process by simply rendering the response as a downloadable inline attachment, if you prefer:
render(:action => :show, :layout => nil, :content_type=> 'application/octet-stream')