Rails send_data doesn't save file - ruby-on-rails

I'm using Nokogiri to generate an XML file in my app. I want to save this file, and I want to display a dialog box in which the user can select the folder in which download this file.
This is the action in my controller:
def download
require 'nokogiri'
if owner_signed_in?
#slips = current_owner.slips
builder = Nokogiri::XML::Builder.new do |xml|
xml.cedolini{
#slips.each do |slip|
xml.cedolino{
xml.codicecliente_ slip.client_code
xml.data_ slip.day.to_s
xml.ordini{
slip.product_slips.each do |order|
xml.ordine {
xml.codicearticolo_ order.product_code
xml.descrizionearticolo_ order.product_description
xml.ammontare_ order.amount.to_s
}
end
}
}
end
}
end
file = builder.to_xml
send_data file, :type => 'text/xml; charset=UTF-8;', :disposition => "attachment; filename=db.xml"
end
end
I have the route defined in this way:
get '/dbsinc/download'
When I call the action from the view, it doesn't save the XML, I see a new page with the url of my action, and in the page I see the XML file rendered on the page, but it doesn't open any dialog box to save the file.
Where am I getting wrong? Thanks

I think I figured out, route: `post 'dbsinc/download'
And in my view I defined the link in this way:
<%= link_to 'Download ', {controller:'dbsinc', action:'download'}, method: :post %>
And it works, the download dialog opens when I click on the link.

Related

Send_file open file in browse instead of download it

Please tell me how this works, because I don't understand... Here is my simple code:
Route:
get 'download' =>'pages#download'
My action:
def download
send_file "#{Rails.root}/public/downloads/robots.zip", :type=>"application/zip"
And download link:
<%= link_to "download", download_path %>
So, when i'm clicking on the link I get this:
Image http://joxi.ru/GrqvQ3xHlbaYmz.jpg
It seems like the browser is trying to show zip file content... But when I open the action from my browser (or refresh page) it's working fine.
Why doesn't it work when I click on the link generated by link_to?
Adding disposition: "inline" instead of disposition: "attachment" worked for me.
Send_file open file in browse instead of download it
Try with this
def download
send_file("#{Rails.root}/public/downloads/robots.zip", :type=>"application/zip",:disposition=> "attachment; filename=robots.zip")
end
and
<%= link_to "download", download_path ,target: '_self' , data-turbolinks="false" %>

Rails send_file rendering files as plain text on new page instead of browser downloading file

When send_file is called, it's sending the file to the browser, but the browser is dumping the contents as plain-text on a new page instead of downloading the file. If I refresh that page, it then downloads the file as normal.
Route
get 'download' => 'qr_codes#download'
Controller
def download
path = Rails.root.join("events/active/#{params[:name]}/#{params[:batch]}/#{params[:file]}")
send_file(path, type: 'application/vnd.ms-excel', filename: params[:file])
end
View
<%= link_to 'Original Upload', download_path(name: #event_name,
batch: batch, file: batch_upload_filename(#event_name, batch)) %>
SOLUTION:
This ended up being a known issue with turbolinks. If using Turbolinks 5 like I am, the updated syntax is: data: { turbolinks: false }
This ended up being a known issue with turbolinks. If using Turbolinks 5 like I am, the updated syntax is:
data: { turbolinks: false }
Try setting the disposition:
def download
path = Rails.root.join("events/active/#{params[:name]}/#{params[:batch]}/#{params[:file]}")
send_file(path, type: 'application/vnd.ms-excel', filename: params[:file], disposition: 'attachment')
end
Or changing the file to ensure the extension is correct
"#{params[:file][0,params[:file].index(".")]}.xlsx"
Oh and don't inject params into a string to build routes for downloading. I can inject "../../" into :name, "config", into :batch, and "../config/database.yml" into :file. Add the file path to a model.
make helper method
def some_helper(content_type)
Rack::Mime::MIME_TYPES.invert[content_type].gsub(/\./mi, '')
end
and update link as
<%= link_to 'Original Upload', download_path(name: #event_name, batch: batch, file: batch_upload_filename(#event_name, batch, format: file_format(attachment.file.content_type))) %>

What happend after send_file in Rails controller?

In the index view, there is a link to download file:
<%= link_to filename, listing_download_path(:file => filename) %>
In the controller:
def download
pathname = File.join(USER_FOLDER, params[:file])
if File.file?(pathname)
send_file pathname
end
end
end
When the user click download, a file download popup is shown. What's happen after the file is downloaded? Does rails just sit there and do nothing more? If I delete the send_file line, dwonload.html.erb will be rendered. Does send_file skip view rendering?
What if I want to show soemthing like "You have downloaded ..."?
The send_file is a render itself. You could use the approach proposed in this question:
Rails: send_file never renders page or DoubleRender error
Basically your download link will send to a "success" view, from which you call the download file method automatically.

How do I create a temp file and write to it then allow users to download it?

I'm working on my first application and I need some help with allowing my users to download a text file with certain variables that are being displayed on the page.
Take a shopping list for example.
Let's say you allow your users to create a shopping list of products, and then display the shopping list with the items on a shopping list page,
e.g. localhost:3000/list/my-list
Take a look at the example code below (which is probably incorrect):
File.open('shopping_list.txt', 'w') do |file|
file.puts 'Item 1: #{product_1.name}'
file.puts 'Item 2: #{product_2.name}'
file.puts 'Item 3: #{product_3.name}'
end
Which then creates a text file that has the following content:
Item 1: Eggs
Item 2: Butter
Item 3: Bread
Users should then be able to download this file (i don't want this file to be stored on the server) via a download link.
I have no idea how to achieve this, but I'm hoping you guys can guide me. :D
TL;DR
create text files populated with model data (perhaps create a method to achieve this?)
text files should not be stored on the server, but created as users click the download button (not sure if this is the rails way but perhaps someone could show me a better way)
I am assuming there is a resource for List with the attribute name as the name of the list and a list has_many Item which has an attribute description
First off, create a download path change your routes config/routes.rb
resources :lists do
member {get "download"}
end
Now if you run a rake routes in the console you should see a route like
/lists/:id/download
Whats more you should now have the helpers download_list_url & download_list_path to use in your view like
<ul>
<% #lists.each do |list| %>
<li> <%= list.name %> - <%= link_to 'Download List', download_list_path(list) %> </li>
<% end %>
</ul>
In your lists_controller add the action, and as you dont actually want to keep the file on the server disk just stream the data as a string
def download
list = List.find(params[:id])
send_data list.as_file,
:filename => "#{list.name}.txt",
:type => "text/plain"
end
Finally you see I have used a as_file method which you should add to the model (I prefer not to do this stuff in controllers, fat models, skinny controllers). So in the List model
def as_file
output = [self.name]
self.items.each {|item| output << item.description }
output.join("\n")
end
You say you don't want to store the file on the server, but "download" it on request; this sounds like you just want to generate and deliver a text document in response to the download link. There are several approaches, but you want to be sure of setting the mime-type so the browser sees it as a text file instead of an html document.
product_info = [
"Item 1: #{product_1.name}",
"Item 2: #{product_2.name}",
"Item 3: #{product_3.name}",
].join("\n")
render :text => product_info # implies :content_type => Mime::Type["text/plain"]
BTW, your example with open/puts above won't output what you think since single-quoted strings don't interpolate.
so, you wish to :
create text files populated with model data (perhaps create a method
to achieve this?)
text files should not be stored on the server, but
created as users click the download button (not sure if this is the
rails way but perhaps someone could show me a better way)
You have the right idea, here's what to do :
Create a method in your model to generate the text file contents. Let's say this method is called list_data
It seems like you have an existing controller action called my_list. Hence we can call our new method in the controller like so :
.
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end
To link to the download, just use link_to :action => my_list, :format => 'text'
See http://api.rubyonrails.org/classes/ActionController/DataStreaming.html#method-i-send_data for full docs on send_data
Caveat & explanations : Using the method above, there isn't really an explicit creation of files, Rails is streaming it for you. Hence this method is not suitable for very large files, or when the generation of the file content will take a while. Use a delayed method to generate the file and store it - the file contents somewhere if that's the case - but we can use send_data once it has been generated
You could try a combination of TempFile and send_file. In your controller action ..
file = Tempfile.new('foo')
file.write("hello world")
file.close
send_file file.path
At Rails 2.3 you can use Template Streaming. Working with Redmine I can remember something like that, you have to adapt for your case. Reference: Streaming and file downloads
require "prawn"
class ClientsController < ApplicationController
# Generate a PDF document with information on the client and return it.
# The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
end
private
def generate_pdf(client)
Prawn::Document.new do
text client.name, :align => :center
text "Address: #{client.address}"
text "Email: #{client.email}"
end.render
end
end
Using the Thong Kuah you must just change the "content_type" param:
def my_list
# pre-existing code
respond_to do |format|
format.html # show html page as before
format.text do
send_data #list.list_data, :content_type => 'text/plain', :filename => 'my-shopping-list.txt'
end
end
end

Force download of file in the public directory from a controller action?

I've done this:
<% response.headers['Content-Disposition'] = "attachment;
filename=somefile.txt" -%>
I am a text file!
I'd like to force the download of a file in my public folder without revealing the path, so I've got a controller than checks some params to know the location of my file (in my public folder) and then I'd like to force the download:
<% response.headers['Content-Disposition'] = "attachment;
filename=#{#invoice.file_name}" %>
How do I get the file content to be here rather than this text?
Is there a way to do that?
I think that send_file would do what you want.
send_file '/path/to.file', :type => 'text/plain', :disposition => 'inline'
Defining the headers isn't the view's job. Doing it in the controller would be much cleaner.
In fact you don't need any html view to render that kind of files.
Doing something like this would be more appropriate :
def action
response.headers['Content-Disposition'] = 'attachment; filename=somefile.txt'
return render(:text => File.read('/path/to/your/file.txt')
end
You keep your thing clean (not having job code in your view) and appropriately force the download on your file.

Resources