Rails downloads PDF instead of displaying it - ruby-on-rails

In my Rails app I'm using Faraday to connect with 3rd party API. In one of the endpoint I'm downloading the pdf which means that the API will send me a binary file as a response. Now I want to display this pdf to the user, to make it work I've got below controller action:
def document
file = client.document(params[:id])
send_data(file, disposition: 'inline', type: 'application/pdf')
end
But instead of displaying the PDF, the download is started. What did I missed?

Found the solution, maybe it will be helpful for someone in the future.
All I need was to set Content-Disposition headers like below:
def document
file = client.document(params[:id])
send_data(file, disposition: 'inline', type: 'application/pdf', headers: { 'Content-Disposition' => 'inline' })
end

Related

Invalid/damaged download using send_data with PowerPoint

I am generating a PowerPoint using the powerpoint gem and up until now I was using send_file, but now I want to use send_data. The reason for this is that send_data blocks the application from doing anything else until the call completes, while send_file allows the app to continue executing while the file downloads, which sometimes, due to coincidental timing, leads to the file being deleted before the user tells their browser to start the download and the web server finishes sending the file and thus a blank or incomplete file is downloaded.
This is how I create my PowerPoint:
#Creating the PowerPoint
#deck = Powerpoint::Presentation.new
# Creating an introduction slide:
title = 'PowerPoint'
subtitle = "created by #{username}"
#deck.add_intro title, subtitle
blah blah logic to generate slides
# Saving the pptx file.
pptname = "#{username}_powerpoint.pptx"
#deck.save(Rails.root.join('tmp',pptname))
Now, on to what I have tried. My first instinct was to call as follows:
File.open(Rails.root.join('tmp',"#{pptname}"), 'r') do |f|
send_data f.read, :filename => filename, :type => "application/vnd.ms-powerpoint", :disposition => "attachment"
end
I've also tried sending the #deck directly:
send_data #deck, :filename => pptname, :disposition => "attachment", :type => "application/vnd.ms-powerpoint"
Reading it ends with a larger file than expected and it isn't even able to be opened and sending the #deck results in a PowerPoint with one slide that simply has the title: #Powerpoint::Presentation:0x64f0dd0 (aka the powerpoint object).
Not sure how to get the object or file into binary format that send_data is looking for.
You need to open the file as binary:
path = #deck.save(Rails.root.join('tmp',pptname))
File.open(path, 'r', binmode: true) do |f|
send_data f.read, filename: filename,
type: "application/vnd.ms-powerpoint",
disposition: "attachment"
end
This can also be done by calling the #binmode method on instances of the file class.
send_data #deck, ...
Will not work since it calls to_s on the object. Since Powerpoint::Presentation does not implement a #to_s method you're getting the default Object#to_s.

Rails send_data creating blank file from controller

I am trying to download a file filled with a string. However, no matter which way I try, the file ends up blank.
Here is the relevant code:
def export(notes)
stream = render_to_string(:template=>"cards/export.enex.erb", :locals => {:notes => notes}, :formats => [:enex])
send_data(stream.to_s, :filename => "notes.enex")
end
I have been using Rails.logger.info to try to track down the problem and have confirmed that stream is not empty and (when prompted to) my log shows that the file was sent full of the correct data. I am using a custom mime type (enex) and that is all set up correctly in config. I've tried several different methods and nothing works. Here are some other attempts:
(1)
#notes = notes
render file: "cards/export", formats: [:enex], type: 'text/plain', disposition: 'attatchment; filename=cards.enex'
(2)
render template: "cards/export", formats: [:enex], :locals => {:notes => notes}, type: 'text/plain', disposition: "attatchment", filename: "notes.enex"
(3)
send_file 'app/views/cards/export.enex.erb', type: 'application/enex', disposition: "attachment; filename=notecards.enex", :x_sendfile=>true
In each case, the file ends up blank.
As you can see, the string I am using is created by filling out an erb form. If it matters, "notes" is a hash that I use to fill out the form. I know how to get this to work by using a button on a view and a respond_to in the controller but I am purposely not using the database and would prefer to solve the problem using a private controller method as shown.
I am using Rails 4
Can you see anything that would cause the send_data to fail?
Try using send_file but instead of giving it the path to the erb, give it the path to the file you generated with that template and specify the type as application/xml or text/xml. I don't have experience with enex files, but I think this may simplify the issue.
file = "my_file.enex"
File.open(file, "w") do |f|
f << render_to_string(:template=>"cards/export.enex.erb", :locals => {:notes => notes}, :formats => [:enex])
end
send_file(file.path, type: "application/xml")

rails: create multiple download files from a controller#action?

I would like to create 2 pdf documents from a single click.
I have it working with one file, but I can't seem to get to make 2 download files to happen w/out browser complaining about DoubleRenderError.
This is the code in my controller, where I am calling it twice and breaks. If I do it just once, everything works fine.
respond_to do |format|
format.pdf do
pdf = PrintCardsFront.new(paperSize, cardCount, #cards)
send_data pdf.render, filename: "cards_front_side.pdf", type: "application/pdf"
pdf = PrintCardsBack.new(paperSize, cardCount, #cards)
send_data pdf.render, filename: "cards_backside_side.pdf", type: "application/pdf"
end
end
(Rails 4.2)
You can only send one file in the HTTP Protocol.
Try to zip your two files and send them to the user.

download csv file to computer

From ajax I call my export action in my controller
def export
data_filtered = eval(params[:data_filtered][9..-2])
CSV.open("data.csv", "wb") do |csv|
csv << data_filtered.first.keys
data_filtered.each do |hash|
csv << hash.values
end
end
end
Currently this does indeed create a file called data.csv on my computer but it doesn't show any evidence of actually doing so. I want the file to be downloaded by the browser and show up in the browser downloads and also in my downloads folder.
EDIT:
def export
data_filtered = eval(params[:data_filtered][9..-2])
csv_file = CSV.generate({}) do |csv|
csv << data_filtered.first.keys
data_filtered.each do |hash|
csv << hash.values
end
end
send_data csv_file, :type => 'text/csv; charset=iso-8859-1; header=present', :disposition => "attachment; filename=tester1.csv"
end
You need to set headers to indicate what the browser should be doing with the resulting file.
send_data file, :type => 'text/csv; charset=iso-8859-1; header=present', :disposition => "attachment;data=#{csv_file}.csv"
However, it would have to be written first, to the server, and then downloaded. You might find this answer helpful: Ruby 1.9.2 export a CSV string without generating a file
Well it appears you are not sending back any response from your controller method, which is why nothing is happening in the browser. You are just processing the CSV.
To send back a downloadable entity a response, one way would be to use either send_data or send_file, depending on what format you have the data in (binary vs. a file).
Both essentially do the same -- allow you to specify the data, the data type, and a downloadable file name that you specify. It can be returned as a response, just like render calls.
Hope that helps!

When using redirect_to files on s3. How to either display an image or download a file

In Rails 3 AttachmentsController, I have the following:
def show
attachment = Attachment.find_by_id(params[:id])
redirect_to(attachment.authenticated_url())
end
Where authenticated_url is simply a URL to S3 to access the file.
The problem is that the file is always downloaded by the browser. What I would like to have happen is if the file is an image/pdf, something the browser can render, show the file in the browser and only download non-browser friendly files.
Have you seen this before? Any ideas on where to start?
Thanks
send_file can be used for remote url as well
file = open("http://cdn2.example.com/somefile.pdf")
send_file(file, :filename => "example.pdf", :type => "application/pdf" , :disposition => "attachment")
Here example.pdf will be downloaded. If you want open pdf in browser itself use this
file = open("http://cdn2.example.com/somefile.pdf")
send_file(file, :filename => "example.pdf", :type => "application/pdf" , :disposition => "inline")
redirect_to #attachment.url
I'm using Paperclip as well, and this display pictures inside the browser. Do you have to do something different than this?
I think you'll want to look into the rails send_file method.
1) http://apidock.com/rails/ActionController/DataStreaming/send_file
The :disposition option lets you decide whether a file will be downloaded or displayed inline. I used this in my rails app to let users download mp3 files.
Hope this helps.

Resources