rails 3 and PDFkit - ruby-on-rails

I'm trying to follow this tutorial.
When I'm adding .pdf to my url it does nothing. My controller has:
respond_to :html, :pdf.
My mime type has been declared.
I tried this too:
respond_to do |format|
format.html
format.pdf {
html = render_to_string(:layout => false , :action => "www.google.fr")
kit = PDFKit.new(html)
send_data(kit.to_pdf, :filename => "candidats.pdf", :type => 'application/pdf')
return # to avoid double render call
}
end
but it does not work and I don't get errors. My browser keep waiting for localhost, but nothing happens.
So how should I try to use pdfkit ?
edit 2 :
According to my rails' logs, rails render successfully the HTML. I saw this in the .log, rails doesn't send it to webrick nor to my browser. And my browser keeps waiting, and waiting and nothing happen. I only have little pictures here.
edit 3 : My webrick server seem unable to respond to other request, once he starts getting a .pdf version of my url, any ideas ?
edit 4 :
I am using rails 3.1, wkhtmltopdf 0.9.5 (windows installer) and pdfkit 0.5.2

I found a better way to access my .pdf urls even in developpement mode.
# Enable threaded mode
config.threadsafe!
# Code is not reloaded between requests
#config.cache_classes = true
Config.cache_classes is a comment, cause i had some problems when it wasn't. This way pdfkit works wonder even with rails 3.1. But, you don't reload code between requests.
That's not really a problem, cause you first work on your html, and you switch configuration, in order to check the pdf result. This way you don't have to bother about your production database.

Thanks to another stack overflow answer I got part of solution.
This works:
html = '<html><body>Toto de combats</body></html>'
#pdf = PDFKit.new(html)
send_data #pdf.to_pdf, :filename => "whatever.pdf",
:type => "application/pdf",
:disposition => "attachement"
You can replace attachement by inline, so the pdf is displayed in your browser.
In the stack overflow answer I spoke about (I don't remember the link), the .to_pdf was missing, but is mandatory. Otherwise PDF reader doesn't recognize it.
I'm trying to get this work with a .pdf url.
Edit 3:
My problem with .pdf urls is solved. known issue with rails 3.1, but google was unable to find them.
explanation : explanation
Workaround (hadn't tryied yet). workaround

Related

Clarification needed for what send_data disposition: "inline" does

I am migrating some codebase from an old rails app into a new one and one of the controller methods has this in it.
send_data(render_compare_chart(#projects, start_date, end_date),
:disposition => 'inline', :type => 'image/png' )
I looked up the definition of what send_data does and I get it except for this part for the disposition option.
:disposition Suggests to the browser that the file should be displayed inline
What exactly does that mean in the context of the code above? Am I supposed to see an image file rendered on the browser or not? Currently I don't see anything that remotely looks like what I'm supposed to be seeing. I don't know what I'm looking for here?
Inside the render_compare_chart method there is some imagemagick stuff going on so I'm wondering if I should be seeing something or not. Can someone clarify what this inline option means? Thanks.

Rails + X-Sendfile serving large video to Ipad

I have an application serving large (some hundreds of MB) video files and it is working perfectly on desktop browsers, using Rails + X-Sendfile on Apache.
An important requirement is that these videos must be private and visible only to logged users, so that's why i'm using Rails to serve them.
Everything works perfectly with other devices. I serve the videos in this way:
response.headers["X-Sendfile"]= filename
send_file filename, :disposition => :inline, :stream => true, :x_sendfile => true
But Ipad's requests need the byte range header. A solution (that does not work perfectly) is something like this:
size = File.size(filename)
bytes = Rack::Utils.byte_ranges(request.headers, size)[0]
offset = bytes.begin
length = bytes.end - bytes.begin
response.header["Accept-Ranges"]= "bytes"
response.header["Content-Range"] = "bytes #{bytes.begin}-#{bytes.end}/#{size}"
send_data IO.binread(filename,length, offset), :type => "video/mp4", :stream => true, :disposition => 'inline', :file_name => filename
With this solution I have problems with larger than 50mb videos, and, more important, I'm giving to rails a responsibility that it shouldn't have. It should be apache to handle the heavy load of the streaming, through the x-sendfile module. But I dont know how. the send_data method does not have the x-sendfile parameter, and solutions involving the send_file method are not working.
I found these 2 questions similar to mine but they are not working: rails media file stream accept byte range request through send_data or send_file method, What is the proper way to serve mp4 files through rails to an Ipad?
Any clue on what's going on? I'm struggling with this since weeks and i need to make it work. Other feasible solutions are welcome.
This could be completely unrelated as I am using nginx for the server, but if its only not working for ios, take a look at this blog post. There could be a similar solution for Apache.
In a sense, I had to add a proxy header that internally redirects to a folder path. How ever stupid this may seem, Apple has some sort of privacy issues that make this necessary for playback with audio and video files. Again not sure if this is the solution for you but for nginx this did wonders and cured my month long headache.
Have you enabled X-Sendfile config in your environment? Include the line config.action_dispatch.x_sendfile_header = "X-Sendfile" for Apache. The server will then use that header for sending files.
Check that the apache request body size is large enough too.

rails + carrierwave S3: force link_to to download

Question:
I would like to force link_to to download images and pdfs fetched from S3 instead of opening them in the browser window.
link_to File.basename(asset.attachment.path), asset.attachment_url.to_s
I looked for solutions but the only ones I found are to handle it in the controller using send_file or send_data but these did not work for me. Finally I stumbled upon the solution in Carrierwave sources.
Solution:
This is what works super well. Use 'response-content-disposition' as a parameter to url
link_to File.basename(asset.attachment.path), asset.attachment_url(:query => {"response-content-disposition" => "attachment"}).to_s
Find more options here: https://github.com/carrierwaveuploader/carrierwave/blob/5aec4725a94fca2c161e347f02b930844d6be303/lib/carrierwave/uploader/versions.rb (line 185)
You can default this for all files of a particular uploader by adding a fog_attributes method.
e.g.
# your_uploader.rb
def fog_attributes
{'Content-Disposition' => "attachment"}
end
This works with requests that aren't signed as well!

Is there a way to have a resque job push a generated pdf directly to the browser?

I need to have a system where a PDF is generated dynamically, asynchronously, and directly pushed to the browser, no disk storage is available. Getting resque to use prawn seems easy, its taking that data and sending it to the browser without storing it somewhere first, I can't find anything online. I thought about Faye, but can Faye handle pushing a PDF to the browser?
I've done this before in .net where i have an iframe's src attribute set to a service that returns a stream. The service aslo flips the http header to content-inline so that the browser doesn't try to download it but instead will try to render it inline. If you try to do this it wont work if the browser doesn't have a pdf plugin (must modern ones will but you always have that guy using IE6 yet) I don't know a lick of ruby but think you should be able to do something similar, or at least but an iframe on a page that targets a service written in something else.
u can use "PDFkit" for it.
the sample code is
def some_action
...
respond_to do |format|
format.html
format.pdf do
generate_pdf(file.html.haml, :css => [array of css file names that need to be added])
end
end
end
in application controller -
def generate_pdf(template, options={})
html = render_to_string(template, :layout => false)
kit = PDFKit.new(html, :orientation => 'Landscape')
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/default_css1.css"
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/default_css2.css"
# Add CSS
if options[:css]
options[:css].each do |css|
kit.stylesheets << "#{Rails.root}/app/assets/stylesheets/#{css}.css"
end
end
send_data(kit.to_pdf, :filename => 'latest.pdf', :type => 'application/pdf', :disposition => 'inline')
end
How big those PDFs are? Your database has BLOB columns (if you don't have storage, you are not using SQLite...) and you can store the resulting PDF in it.
Or you can store the resulting PDF in the Redis DB. Or save it in S3.
On the other end, the browser will be polling (with ajax) every now and then to know whether the PDF is complete, and as soon as it is ready it will download it and show it to the user.

Rails 3 + PDFKit: How to convert a view to PDF?

In my Rails 3 application on Windows I have the following page which shows a job receipt and lets user to edit it:
http://localhost:3001/jobs/45/invoice
I have also a "Create PDF" button in the bottom of the page. When pressed, create_pdf_invoice of my JobsController is called:
def create_pdf_invoice
job = Job.find(params[:id])
kit = PDFKit.new("<h1>Hello</h1><p>This is PDF!!!</p>", :page_size => "A4")
file = kit.to_file("my_file_name.pdf")
redirect_to(:action => 'index')
end
end
All this works fine, i.e. the PDF is created!
My question is how can I print the invoice itself rather than this static text (like if I press "Print" on the http://localhost:3001/jobs/45/invoice page) ?
UPDATE
I tried to put
require 'pdfkit'
and
config.middleware.use PDFKit::Middleware
in config/application.rb as suggested here.
The server starts normally, but when I go to
http://localhost:3001/jobs/45/invoice.pdf
Ruby crashes:
I use:
ruby 1.9.2p0 (2010-08-18) [i386-mingw32]
Rails 3.0.1
rake, version 0.8.7
pdfkit (0.5.0)
Any ideas ?
first you need to tell the application to use pdfkit as a middleware.
So somewhere in an initializer you need to put:
# PDFKit
require 'pdfkit'
middleware.use PDFKit::Middleware
PDFKit.configure do |config|
config.wkhtmltopdf = 'windows_path_to_wkhtmltopdf'
end
After this if you call
http://localhost:3001/jobs/45/invoice.pdf
a pdf should be generated for you.
PDFkit is a middleware that intercepts the pdf format rendering the page accordingly.
If you want you can also restrict pdfable routes in the config through regexes or string.
Just a caveat, if you have images in the page they must be called with an absolute path.
We are also finding some problems with pdfkit 0.5.0 but things are working fine with version 0.4.6 it is something to do with paths so maybe it can solve your issues.
With the help of PDFKit middle-ware you can only view the html page in pdf format in the browser.
If want to show the download prompt then you have to set the header in your action.
headers["Content-Disposition"] = "attachment; filename=export"
Further, If you want to save the pdf file in server then you can try this link.
Save PDF file shown by PDFKit middleware

Resources