1 分鐘閱讀

[Rails] 10分鐘內使用HTML模板產生PDF

原文出處 How to Generate PDFs with HTML templates in 10 minutes with Rails

前言

為您的Web程式產生PDF並非你想像中的那麼困難。

有很多方法可以做到這一點,還有另一個好用gem,叫做Prawn,但是為了今天的教學,我將介紹另一個流行的gem,叫做wicked_pdf。

WickedPDF使用wkhtmltopdf,它允許您直接從HTML產生PDF,而不必遵守DSL。

此方式能夠更快速的達到想要的效果。

安裝步驟

Step 1: 安裝 wkhtmltopdf 和 wicked_pdf gem
# [path] gemfile
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'
Step 2: 執行 Generator
rails generate wicked_pdf

接下來,假設你不是透過Gemfile安裝wkhtmltopdf的binary檔案,你需要確保wicked_pdf知道在哪裡找到它們。

# [path] config/initializers/wicked_pdf.rb
WickedPdf.config = {
  #:wkhtmltopdf => '/usr/local/bin/wkhtmltopdf',
  #:layout => "pdf.html",
  #:exe_path => '/usr/local/bin/wkhtmltopdf'
}

如果你沒有寫清楚,你可能會得到一個“Bad wkhtmltopdf’s path”的錯誤訊息。

OK你已經準備好了!現在有2個方式可以使用wicked_pdf。

  1. 從controller render。
  2. 從自建的library render。

< I > 從controller render

要使用它從controller呈現pdf,您需要做的是在controller方法中新增以下內容。

讓我們以發票應用為例,當用戶訪問/invoices/1時,您想產生一份pdf發票。

首先,將以下程式新增到invoices_controller.rb (或使用其他controller)

我會解釋一下每個參數是什麼意思,但請記住將它們替換為您自己的相關數據!

# [path] invoices_controller.rb
  def show

    @invoice = Invoice.find params[:id]

    respond_to do |format|
      format.pdf do
        render pdf: "file_name_of_your_choice",
               template: "invoices/show.pdf.erb",
               locals: {:invoice => @invoice}
      end
    end
  end

pdf: “file_name_of_your_choice” 這行表示即將輸出的pdf名稱

template: “invoices/show.pdf.erb” 這行表示使用views/invoices/show.pdf.erb當作模板

locals: {:invoice => @invoice} 這行表示將把@invoice傳入模板中

那麼什麼是invoices/show.pdf.erb

<!-- [path] show.pdf.erb -->
<!-- app/views/invoices/show.pdf.erb -->

<h1><%= invoice.title %></h1>
<p><%= invoice.description %></p>
<p><%= invoice.amount %></p>

就像一般的htm.erb一樣,你可以使用pdf.erb,它會把它渲染到你的pdf。

如果您注意到上述程式碼,我們增加了本地參數。就像渲染部分一樣,這將允許您在view中使用您的變數。

這就是全部了。當用戶訪問/invoices/1時,就會產出一個pdf。

< II >從自建的library render

可能有些情況下您想要取得pdf代碼。你不一定要用它作為view的替代顯示。

在這種情況下,您將需要外部渲染您的pdf。

例如,讓我說我有一個外部庫將生成PDF,我想使用生成的PDF作為電子郵件附件。

我們該怎麼做?

首先,讓我們創建一個庫和一個類的方法。

# /lib/pdf_generator/bot.rb

module PdfGenerator
  class Bot
    def self.receipt(payment_details)
    end
  end
end

好的,現在可以增加程式碼。

所以,如果你在前面的例子中注意到,我們正在使用 ActionController render PDF:…。

# invoices_controller.rb
def show
 ..
   render pdf: "file_name_of_your_choice",
 ..
end

該PDF PARAMS是非常重要的。這告訴Rails應用程序使用WickedPDF的渲染方法來解析和創建PDF。

問題是,你不能調用ActionController外的渲染,那麼我們如何從外部的圖形庫渲染?

很簡單,我們把ActionController帶出來並使用render_to_string方法

# in bot.rb
..
class Bot
  def self.receipt(payment_details)
    # 先宣告一個ActionController
    ac = ActionController::Base.new

    # 然後,我們可以複製之前的範例的輸出方式。
    pdf = ac.render_to_string pdf: "file_name_of_your_choice",
               template: "invoices/show.pdf.erb",
               locals: {:myvariable => variable}
  end
end
..

進階說明 如果想要使用Application Helpers, 你可能會發現你的helpers無法在你的templates中使用。 解決方式,請以ApplicationController.new取代ActionController::Base.new。這樣就可以了!

但等等,這不是只是一個字串嗎?字串有多有用?

那麼現在你可以建立一個臨時文件來存儲pdf。

# in bot.rb
..
    pdf = ac.render_to_string pdf: "file_name_of_your_choice",
               template: "invoices/show.pdf.erb",
               locals: {:myvariable => variable}
    
    # lets create a Tempfile which we will use to generate an actual file to be
    # attached to an e-mail
    tempfile = Tempfile.new "anynamehere.pdf"
    tempfile.binmode
    tempfile.write pdf
    tempfile.close
    tempfile
..

所以現在PdfGenerator::Bot.receipt接收一個對象,將其解析成一個視圖,並返回產生一個漂亮的臨時文件PDF。

使用該tempfile,您可以附加到郵件程式中。

例如:

# example_mailer.rb
def payment_details(details)
  attachments["anyfilenametoo.pdf"] = File.read(PdfGenerator::Bot.receipt(details).path)
  mail(subject: .....)
end

希望這可以幫助到大家!

參考

  1. http://dchua.com/2014/10/30/generate-pdfs-with-html-templates-in-rails/

更新時間:

留言