Downloading PDF's using PDFkit error on Rails

1.6k views Asked by At

I am trying to download an HTML view in Rails5 as a PDF, however, whenever I download the PDF I get an error: Failed to load PDF document. I am using the following gems:

gem 'pdf kit' gem 'wkhtmltopdf-binary'

here is my controller:

class InputController < ApplicationController

def index
    @inputs = Input.all
end


def show
    @input = Input.find_by(id: params[:id])

            send_data @pdf, :filename => @input.company + ".pdf",
                                    :type => "application/pdf",
                                    :disposition => "attachment"
end


end

when i type in a download URL ie: localhost:3000/input/1.pdf i get a downloaded pdf file with an error:

enter image description here

My show view is very simple:

<ul>
<li><%= @input.company %></li>
<li><%= @input.position %></li>
<li><%= @input.date %></li>
</ul>

Any help would be appreciated.

Best, Ayaz

UPDATE

I also just tried taking out @pdf and putting in @input:

    def show
    @input = Input.find_by(id: params[:id])
            send_data @input, :filename => @input.company + ".pdf",
                                    :type => "application/pdf",
                                    :disposition => "attachment"
    end

No change in results

1

There are 1 answers

4
7stud On BEST ANSWER

Your code doesn't assign anything to the instance variable @pdf:

def show
  @input = Input.find_by(id: params[:id])

  send_data @pdf, :filename => @input.company + ".pdf",
                  :type => "application/pdf",
                  :disposition => "attachment"
end

and instance variables have a default value of nil. You need to do something like:

input = Input.find_by(id: params[:id])

html = %Q{
<ul>
<li>#{input.company}</li>
<li>#{input.position}</li>
<li>#{input.date}</li>
</ul>
}

kit = PDFKit.new(html, :page_size => 'Letter')
pdf = kit.to_pdf


send_data pdf, :filename => input.company + ".pdf",
               :type => "application/pdf",
               :disposition => "attachment"

Or, if you want to read the view file then run it through the ERB engine, something like this:

file_path = File.expand_path("../views/input/show.html.erb", __FILE__)
erb_template = File.read(file_path)

@input = Input.find_by(id: params[:id])
renderer = ERB.new(erb_template)
html = renderer.result()

#As above...

From the Rails Guide:

By default, controllers in Rails automatically render views with names that correspond to valid routes.
...
...
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.

And from the docs for send_data():

This method is similar to render plain: data, but also allows you to specify whether the browser should display the response as a file attachment (i.e. in a download dialog) or as inline data

Therefore, when you call send_data() in the controller, you are explicitly rendering something, so the default rendering of the action_name.html.erb file doesn't occur.