pdftk, tempfile and rails - ruby-on-rails

Ok, I've tried all kinds of stuff and I'm not entirely sure this will work. The pdfs I need to merge are on the server and the links to them are hardcoded. pdftk works locally in my terminal, but not with these remote links. So I'm unsure if this will work once I get it up to production.
Basically, I'm trying to write a method that will retrieve a selected group of pdfs and merge them into one pdf for the user to download.
But I'm having a hard time deciphering how to utilize tempfiles and running terminal commands through the rails app.
Here is the method:
def create
#routes = TransitRoute.find(params[:selected_routes])
#selected_routes = #routes.collect {|x| x.new_pdf_link + " "}
Tempfile.open('prefix', "#{Rails.root}/tmp") do |f|
f.print("pdftk #{#selected_routes} cat output temporary.pdf")
f.flush
f.read
end
respond_to do |format|
format.html
end
end
I have a couple questions:
My tempfile has the correct command line written to it:
pdftk 1.pdf 2.pdf cat output new.pdf
How do I get this line run so that the new.pdf is created?
Am I supposed to replace the tempfile with the new pdf, or write the new pdf to it or just make the new pdf in it's own location? If the latter, how do I get it to be temporary?
How do I get a link to the new pdf so users can download it?
Some of this may be basic stuff, but I've never had to mess with tempfiles of making pdfs dynamically like this.
Oh yeah, and this app is also in Rails 2.3
Thanks for any help.

Ok, I have it working. Here's the new code incase someone has advice for improvement or has the same question:
def create
file = Tempfile.new('temp_route_pdf', "#{Rails.root}/tmp/")
#routes = TransitRoute.find(params[:selected_routes])
selected_routes = #routes.collect {|x| x.new_pdf_link + " "}
route_names = #routes.collect {|x| x.short_name + "_"}
#generated_pdf_file = "#{route_names}routes.pdf"
`pdftk #{selected_routes}cat output #{file.path}`
raise Exception unless $?.success?
send_file( "#{file.path}",
:type => "application/pdf",
:filename => "#{#generated_pdf_file}",
:disposition => 'inline')
end

Related

Allowing client to download files from ftp rails

I'm developping a web application in rails 4 and I'm currenty faced with a tiny issue.
I want to make the users of the website to be able to download files from a ftp by clicking on a link. I've decided to go on this:
def download
#item=Item.find(params[:id])
#item.dl_count += 1
#item.save
url = #item.file_url.to_s
redirect_to url and return
end
And, very basically, this in my view:
<%= link_to 'DL', controller: "items", action: "download"%>
However, I'm not quite satisfied by this, as it generates a few mistake like the fact that clicking the link create two GET methods, one responding by 403 Forbidden and the next with a 302 found...
Do you have any idea about how I could improve this?
In Rails you should do:
def download
#item=Item.find(params[:id])
#item.dl_count += 1
#item.save
url = #item.file_url.to_s
send_file url, type: 'image/jpeg', disposition: 'inline'
end
Take a look for more information http://apidock.com/rails/ActionController/DataStreaming/send_file
Note that send_file can send only from local file system.
If you need get file from remote source (should be secure location) like http://example.com/apps/uploads/tfm.zip and avoid store this file in server memory, you can first save file in #{RAILS_ROOT}/tmp/ or system /tmp and then send_file
data = open(url)
filename = "#{RAILS_ROOT}/tmp/my_temp_file"
File.open(filename, 'w') do |f|
f.write data.read
end
send_file filename, ...options...
If Rails can`t read file, you should check file permission

[Ruby]IO.Read not reading the whole file

I want to download a zip file containing some images via a rails method. The problem is that when I try to read thoses files, I'm not getting the entire file. Of course, that's pretty much annoying to display the image.
Here is my code :
def download_zip(image_list)
if !image_list.blank?
file_name = "pictures.zip"
t = Tempfile.new("lolbite11")
Zip::OutputStream.open(t.path) do |z|
image_list.each do |img|
title = img.name
title += ".jpg" unless title.end_with?(".jpg")
z.put_next_entry(title)
z.print IO.read(Rails.root.join('public', img.path)) <- Here is the problem ?
end
end
send_file t.path, :type => 'application/zip',
:disposition => 'attachment',
:filename => file_name
t.close
end
end
After I've download the zip and extract it. I open my images with a text editor and sadly see that I've not the whole image... I tried to output the 'IO.read' and it's only displaying what is in my final file.
I finally found an other way to solve my problem with File.copy_stream().
But if someone knows why this doesn't work, feel free to explain to me.

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

saving html from ruby on rails output to a variable

I have a ruby on rails website. The page is dynamically loaded and generated using ruby and rails. However, I'd like to also generate a static .html page to ease my server rather than calling the rails page every time.
In PHP I know how to capture the output buffer using ob_start() and ob_get_contents() to get the outputted text.
How do I capture the output from my rails page into a variable?
EDIT: The reason I want to do this is so that I can save my page as .html for use on other machines. So I generate the HTML using ruby and distribute to others in a format they can view.
You should use Rails caching to achieve this result. It achieves the ends you are looking for.
Alternatively, you can render_to_string and output the result using render:
#ticket_controller.rb
def TicketController < ApplicationController
def show_ticket
#ticket = Ticket.find(params[:id])
res = render_to_string :action => :show_ticket
#... cache result-- you may want to adjust this path based on your needs
#This is similar to what Rails caching does
#Finally, you should note that most Rails servers serve files from
# the /public directory without ever invoking Rails proper
File.open("#{RAILS_ROOT}/public/#{params[:action]}.html", 'w') {|f| f.write(res) }
# or .. File.open("#{RAILS_ROOT}/public/#{params[:controller]}/#{params[:action]}/#{params[:id]}.html", 'w') {|f| f.write(res) }
# or .. File.open("#{RAILS_ROOT}/snapshots/#{params[:controller]}/#{params[:action]}/#{params[:id]}.html", 'w') {|f| f.write(res) }
render :text => res
end
end
You probably want to look into caching rather then saving the output of your rails app directly. Check out:
http://guides.rubyonrails.org/caching_with_rails.html
http://api.rubyonrails.org/classes/ActionController/Caching/Pages.html
I ended up going with the following:
#page_data = render_to_string() # read the entire page's output to string
if (File.exist?('../cache.html'))
file = File.open('../cache.html','rb')
contents = file.read
else
contents = ''
end
if (#page_data!=contents) # if the page has changed
# save the output to an html version of the page
File.open('../cache.html','w') {|f| f.write(#page_data) }
end

Problems with Prawnto options

I'm using Prawnto to generate PDFs in my Rails app. I want three specific options set for my PDFs:
I don't want it to start with a blank page
I want it to download directly (not inline)
I want to specify the filename
Here's my controller method:
def print
#purchase = Purchase.find(params[:id])
prawnto :prawn=>{:skip_page_creation=>true}, :inline=>false, :filename=>#purchase.deal.name + "-" + #purchase.customer.name+".pdf"
end
Without the :skip_page_creation option, the other two options (inline and filename) work fine. But when I add the skip_page_creation option, it goes inline with a default filename. And of course, if I remove skip_page_creation, I get a nice downloaded PDF with a first blank page.
The docs for this library leave something to be desired, but can anyone point me in the right direction?
Cheers!
Aaron.
I've just tried this by changing one of my inline examples which worked ok:
module SharedPdfs
def show
prawnto :prawn => {:skip_page_creation=>true}, :inline => false, :filename => "results_pdf.pdf"
render :template => '/results/show'
end
end
Had a quick look at the prawnto source and it should pickup your prawn options not sure why it isn't but at least you've got it working for now.

Resources