question about creating a link to download - ruby-on-rails

I'm new to Rails and working on a project where after the user logs in
they can click on a link to download an exe file ( say for example the
file is at http://test.com/test.exe). I want to keep the link to the exe
file hidden. What are the best ways to implement this step.
I was thinking about using the redirect to url but I have been getting
an error saying that I cannot use two redirect's in a function.
Also, I was thinking of using NET:HTTP to use http request but I have no
idea how to implement this.
Thanks!!

You should read the file in your code and output it as-is to the client.
Send before it the relevant headers:
Content-type: application/octet-stream
Content-Disposition: inline; filename=[file name you want the user to see]
Content-length: [file size]
Why doing two redirects in same place is bad, it is like trying to steer a car in two directions at once...
It might be better to use
Content-Type: application/force-download
instead of
Content-type: application/octet-stream

Seems like no one has mentioned X-Sendfile.
File Downloads Done Right

I tried the below code, just an example exe file but the code looks like is reading the whole file which is over 500 MB. I want it to just give me the option to save the exe file Thanks!
def download
send_data(open('http://mirrors.gigenet.com/ubuntu/intrepid/ubuntu-8.10-desktop-i386.iso').read,
:filename => 'ubuntu-8.10-desktop-i386.iso' ,
:type => 'application/force-download',
:disposition => 'attachment')
end

To proxy the file through your controller, you can use open-uri.
In environment.rb
require 'open-uri'
In your controller
def download
send_data(open(params[:url]).read,
:filename => 'somefilename',
:disposition => 'attachment'
)
end
EDIT: If you don't want to proxy the file, you have to redirect_to(url). If you're getting the message about redirect called twice, then keep in mind that you cannot both render and redirect from the same action. If you need to load a view, and then start a download, use two actions:
def present_download
#download = Download.find(params[:id])
# implicitly calls render :action => :present_download
end
def download_file
#download = Download.find(params[:id])
respond_to do |format|
format.html { redirect_to(#download.url) }
end
end
And in your view (present_download.html.erb):
<html>
<head>
<meta http-equiv="refresh" content="1;url=<%= url_for :action => :download_file -%>" />
</head>
<body>
Your download will automatically start…
…

Related

How to render pdf into web browser in RoR

I have files in server for whom i want to keep the url confidential. For this, i created a controller that fetch the data and ultimately render it to the web broswer.
In the view
<%= link_to "Click to view the file", file_proxy( user.pdf_file_url ) %>
In users_heper.rb
def file_proxy(url)
file_proxy_path(url: url)
end
In the routes.rb
get "file_proxy" => "file_proxy#fetch"
In the controller
def FileProxy < ApplicationController
def fetch
response = HTTParty.get params[:url]
render response
end
end
I'm getting an <HTTParty::Response:0x10cd6e6a8 parsed_response="%PDF-1.3......" is not an ActiveModel-compatible object. It must implement :to_partial_path.
Do you know how to tweak this code so that it can display the PDF file correctly ?
Thanks!
You can't call render that way. It's expecting very specific options. In this case it probably looks like:
pdf_content = HTTParty.get(params[:url])
send_data(pdf_content, disposition: 'inline', type: 'application/pdf')
As a note, you probably want to limit what sorts of things that tool fetches or someone will eventually abuse it.

Rails download http response/displayed site

Instead of displaying the xml file rendered by the index.api.rsb file in my browser, i want to download it. To me this sounds very simple, but I cant find a solution.
I tried the following in the controller-method:
def split
if params[:export] == "yes"
send_file *here comes the path to xml view*, :filename => "filename", :type => :xml
end
respond_to ...
end
The result is a MissingFile exception...
Thanks in advance
Note that :disposition for send_file defaults to 'attachment', so that shouldn't be a problem.
If you have a MissingFile exception, that means the path is incorrect. send_file expects the path to an actual file, not a view that needs to be rendered.
For your case, render_to_string might be what you need. Refer to this related question. It renders the view and returns a string instead of setting the response body.
def split
if params[:export] == "yes"
send_data(render_to_string path_to_view, filename: "object.xml", type: :xml)
end
end
To force it to download it, add :disposition => attachment to your send_file method.
Source: Force a link to download an MP3 rather than play it?

button to save current page in rails 3.2

I need to have a button to save the current web site (just like clicking on "Save as"), I created a method in the controller which works great for any external site (like http://www.google.com) but doesn't work for the sites inside my application, I get a timeout error!. This has no explanation to me :(
Any clue what is the issue?
#CONTROLLER FILE
def save_current_page
# => Using MECHANIZE
agent = Mechanize.new
page = agent.get request.referer
send_data(page.content, :filename => "filename.txt")
end
I tried also Open URI, same problem!
#CONTROLLER FILE
def save_current_page
# => USANDO OPEN URI
send_data(open(request.referer).read, :filename => "filename.txt")
end
I'm using rails 3.2 and ruby 1.9, any help is appreciated, I already spent like 10 hours trying to make it work!!
Rails can only handle one request at a time. It's a never-ending standoff between the two requests - the first request is waiting for the second request, but the second request is waiting for the first request, and therefore you get a Timeout error. Even if you're running multiple instances of the app with Passenger or something, it's a bad idea.
The only way I can think to get around it would be to use conditional statements like so:
referer = URI.parse(request.referer)
if Rails.application.config.default_url_options[:host] == referer.host
content = "via yoursite.com"
else
agent = Mechanize.new
page = agent.get request.referer
content = page.content
end
send_data content, filename: "filename.txt"
A little dirty but it should get around the Timeout problem. As far a getting the actual content of a page from your own site - that's up to you. You could either render the template, grab something from cache, or just ignore it.
A much better solution would be to enqueue this code into something like Resque or Delayed Job. Then the queue could make the request and wait in line to request the page like normal. It would also mean that the user wouldn't have to wait while your application make a remote request, which is dangerous because who knows how long the page will take to respond.
After several hours and lots of other posts I got to a final solution:
Bricker is right in that it is not possible for rails to render more than once in a call, as taken from http://guides.rubyonrails.org/layouts_and_rendering.html "Can only render or redirect once per action"
The site also states "The rule is that if you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controller’s view path and render it."
Then, the solution that worked great for me was to tell the controller to render to a string if a download flag (download=true) was set in :params (I also use request.url to have it working from any view in my application)
View:
= link_to 'Download', request.url+"&downloadexcel=true", :class => 'btn btn-primary btn-block'
Controller:
def acontrolleraction
#some controller code here
if params[:downloadexcel]
save_page_xls
else
# render normally
end
end
def save_page_xls
#TRESCLOUD - we create a proper name for the file
path = URI(request.referer).path.gsub(/[^0-9a-z]/i, '')
query = URI(request.referer).query.gsub(/[^0-9a-z]/i, '')
filename = #project_data['NOMBRE']+"_"+path+"_"+query+".xls"
#TRESCLOUD - we render the page into a variable and process it
page = render_to_string
#TRESCLOUD - we send the file for download!
send_data(page, :filename => filename, :type => "application/xls")
end
Thanks for your tips!

Rails 3 render plaintext page using view

I have a Linux configuration file stored in my database in Rails, and I'd like to be able to download the configuration through a web request
My goal on the Linux side is to curl/wget the webpage, compare it to the current configuration, and then hup the server. Easy enough to do in a script.
In normal circumstances on Rails, you could do
render :text => #config_file
However, I need to do some formatting of the data first to apply the static headers, etc. This isn't a one-liner, so I need to be able to render a view.
I have the following set in my controller, but I still get a minimal set of HTML tags in the document
render(:content_type => 'text/plain', :layout => false);
I've done something similar in .Net before, so it printed out a text file with \n interpreted. How do I get that in Rails?
Normally, this is done with
# config/initializers/mime_types.rb
# ...
# Mime::Type.register "text/plain", :plaintext
# No changes needed as rails comes preconfigured with the text/plain mime type
# app/controllers/my_controller.rb
class MyController < ApplicationController
def my_action
respond_to do |format|
format.text
end
end
end
and a view file
# app/views/my_controller/my_action.text.erb
...
About the minimal HTML you find in the DOM: Are you seeing this from within some kind of in-browser inspector, like the ones included in google chrome or safari? If so then don't worry, this is added by the browser in order to display your text/plain document inline. If you look at the source of the delivered document (ctrl-u) no HTML should show up.

rails how to render a file with correct filename

This is tough one to explain so i'll try my best, and hopefully edit the question if people need more information. I am not providing exact code, but merely an example of the issue.
I am using rails 2.3.8. I am on Unix.
I have a bunch of files under a directory not Apache accessible. (i.e. /data/files/file.rpk)
I have the following in my view.
link_to "RPK File", :controller => 'mycontroller', :action=> 'myaction', :file => '/data/files/file.rpk'
I have the following in my controller.
def myaction
if FileTest.exists?(params[:file])
render :file => params[:file]
end
end
When i select the link on the page i get a download prompt for my desired file, but the name of the file is "myaction" instead of the filename.
Thoughts on how i could get it named correctly?
Sounds like a job for send_file. The x_sendfile option prevents that your workers keep busy while transferring the actual file. You can read more about that in this blogpost.
send_file path_to_file_on_filesystem, :type => "application/zip", :x_sendfile => true
You want to use send_data with the :filename option. See the API documentation.
You want to be extremely careful with this, though. Never ever trust the client/user! They will send file=../../../../etc/group or something in order to read arbitrary files on your system, so be very sure to sanitize that value before passing it to any file-reading methods.

Resources