2013-02-07 9 views
14

In Rails3 verwende ich die WickedPDF gem, um ein PDF-Format eines meiner Modelle zu rendern. Dies funktioniert einwandfrei: /invoices/123 gibt HTML zurück, /invoices/123.pdf lädt ein PDF herunter.PDF von WickedPDF zum Anhängen über Carrierwave herunterladen

In meinem Rechnungsmodell verwende ich das Juwel "state_machine", um den Rechnungsstatus zu verfolgen. Wenn eine Rechnung vom Status "nicht fakturiert" in den Status "fakturiert" wechselt, möchte ich eine Kopie der PDF-Datei der Rechnung abrufen und sie mit CarrierWave an das Rechnungsmodell anhängen.

Ich habe die drei Teile getrennt arbeiten: der Controller erstellt eine PDF-Ansicht, das Modell verfolgt den Status und löst einen Rückruf beim richtigen Übergang, und CarrierWave ist richtig eingerichtet. Allerdings habe ich eine Menge Zeit, sie dazu zu bringen, gut miteinander zu spielen.

Wenn ich nur die HTML-Version der Rechnung greifen wollte, könnte ich render_to_string aus dem Modell anrufen. Aber render_to_string scheint beim Empfangen einer PDF-Binärdatei zu ersticken. Wenn ich einen Datenstrom zurückbekomme, ist es ziemlich einfach, diese Daten in eine temporäre Datei zu schreiben und sie an den Uploader anzuhängen, aber ich kann nicht herausfinden, wie ich den Datenstrom bekomme.

Irgendwelche Gedanken? Code unten:

Rechnungs Controller

def show 
    @invoice = @agg_class.find(params[:id]) 

    respond_to do |format| 
    format.pdf do 
     render_pdf 
    end 
    format.html # show.html.erb 
    format.json { render json: @aggregation } 
    end 
end 

...

def render_pdf(options = {}) 
    options[:pdf] = pdf_filename 
    options[:layout] = 'pdf.html' 
    options[:page_size] = 'Letter' 
    options[:wkhtmltopdf] = '/usr/local/bin/wkhtmltopdf' 
    options[:margin] = { 
    :top  => '0.5in', 
    :bottom => '1in', 
    :left  => '0in', 
    :right => '0in' 
    } 
    options[:footer] = { 
    :html => { 
     :template => 'aggregations/footer.pdf.haml', 
     :layout  => false, 
    } 
    } 
    options[:header] = { 
    :html => { 
     :template => 'aggregations/header.pdf.haml', 
     :layout  => false, 
    } 
    } 
    render options 
end 

Invoice.rb

def create_pdf_copy 

    # This does not work.  
    pdf_file = ApplicationController.new.render_to_string(
    :action => 'aggregations/show', 
    :format => :pdf, 
    :locals => { 
     :invoice => self 
    } 
) 

    # This part works fine if the above works. 
    unless pdf_file.blank? 
    self.uploads.clear 
    self.uploads.create(:fileinfo => File.new(pdf_file), :job_id => self.job.id) 
    end 

end 

UPDATE eine Lösung gefunden.

def create_pdf_copy 

    wicked = WickedPdf.new 

    # Make a PDF in memory 
    pdf_file = wicked.pdf_from_string( 
     ActionController::Base.new().render_to_string(
      :template => 'aggregations/show.pdf.haml', 
      :layout  => 'layouts/pdf.html.haml', 
      :locals  => { 
       :aggregation => self 
      } 
     ), 
     :pdf => "#{self.type}-#{self}", 
     :layout => 'pdf.html', 
     :page_size => 'Letter', 
     :wkhtmltopdf => '/usr/local/bin/wkhtmltopdf', 
     :margin => { 
      :top  => '0.5in', 
      :bottom => '1in', 
      :left  => '0in', 
      :right => '0in' 
     }, 
     :footer => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/footer.pdf.haml', 
       :layout => false 
      }) 
     }, 
     :header => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/header.pdf.haml', 
       :layout => false 
      }) 
     } 
    ) 

    # Write it to tempfile 
    tempfile = Tempfile.new(['invoice', '.pdf'], Rails.root.join('tmp')) 
    tempfile.binmode 
    tempfile.write pdf_file 
    tempfile.close 

    # Attach that tempfile to the invoice 
    unless pdf_file.blank? 
     self.uploads.clear 
     self.uploads.create(:fileinfo => File.open(tempfile.path), :job_id => self.job.id) 
     tempfile.unlink 
    end 

end 
+0

OK, ich habe es funktioniert. In Schritt 1 wurde daran gearbeitet, dass WickedPDF globale Konfigurationseinstellungen ignoriert, wenn es nicht im Kontext eines Controllers läuft. Schritt 2: Verwenden Sie Tempfile, um die PDF-Ausgabe als Binärmodus-TEMP-Datei zu speichern und anschließend an Carrierwave anzuhängen. Bearbeiteter Originalbeitrag, um die Lösung zu reflektieren. – lascarides

+0

Können Sie mir bitte mehr Details zu Schritt 1 geben? Ich hatte eine alte Version von wkhtmltopdf lokal und exportiert mit render_to_string ganz gut. Auf dem Server habe ich wkhtmltopdf 0.11.0, und render_to_string funktioniert, aber PDF ist nicht lesbar. Upgraden auf 0.11.0 lokal und render_to_string Chokes .. Scheint das gleiche Problem zu sein .. FYI: Ich benutze 'file = StringIO (render_to_string (Optionen)) 'So kann ich die Tempdatei überspringen. Dies ist mit Büroklammer, aber Sie könnten es versuchen, wenn Sie die Idee mögen. –

+2

Froh, dass Sie es gelöst haben. Sie sollten Ihre Lösung als Antwort schreiben und dann Ihre Antwort akzeptieren. Es schadet dir nicht, deine eigene Frage zu beantworten! – sockmonk

Antwort

6

Lösung:

def create_pdf_copy 

    wicked = WickedPdf.new 

    # Make a PDF in memory 
    pdf_file = wicked.pdf_from_string( 
     ActionController::Base.new().render_to_string(
      :template => 'aggregations/show.pdf.haml', 
      :layout  => 'layouts/pdf.html.haml', 
      :locals  => { 
       :aggregation => self 
      } 
     ), 
     :pdf => "#{self.type}-#{self}", 
     :layout => 'pdf.html', 
     :page_size => 'Letter', 
     :wkhtmltopdf => '/usr/local/bin/wkhtmltopdf', 
     :margin => { 
      :top  => '0.5in', 
      :bottom => '1in', 
      :left  => '0in', 
      :right => '0in' 
     }, 
     :footer => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/footer.pdf.haml', 
       :layout => false 
      }) 
     }, 
     :header => { 
      :content => ActionController::Base.new().render_to_string({ 
       :template => 'aggregations/header.pdf.haml', 
       :layout => false 
      }) 
     } 
    ) 

    # Write it to tempfile 
    tempfile = Tempfile.new(['invoice', '.pdf'], Rails.root.join('tmp')) 
    tempfile.binmode 
    tempfile.write pdf_file 
    tempfile.close 

    # Attach that tempfile to the invoice 
    unless pdf_file.blank? 
     self.uploads.clear 
     self.uploads.create(:fileinfo => File.open(tempfile.path), :job_id => self.job.id) 
     tempfile.unlink 
    end 

end