Search code examples
ruby-on-railscarrierwaveprawn

How to handle a file_as_string (generated by Prawn) so that it is accepted by Carrierwave?


I'm using Prawn to generate a PDF from the controller of a Rails app,

...
respond_to do |format|
  format.pdf do
    pdf = GenerateReportPdf.new(@object, view_context)
    send_data pdf.render, filename: "Report", type: "application/pdf", disposition: "inline"
  end
end

This works fine, but I now want to move GenerateReportPdf into a background task, and pass the resulting object to Carrierwave to upload directly to S3.

The worker looks like this

def perform
  pdf           = GenerateReportPdf.new(@object)
  fileString    = ???????
  document      = Document.new(
    object_id: @object.id,
    file: fileString )
    # file is field used by Carrierwave 
end

How do I handle the object returned by Prawn (?????) to ensure it is a format that can be read by Carrierwave.

fileString = pdf.render_file 'filename' writes the object to the root directory of the app. As I'm on Heroku this is not possible.

file = pdf.render returns ArgumentError: string contains null byte

fileString = StringIO.new( pdf.render_file 'filename' ) returns TypeError: no implicit conversion of nil into String

fileString = StringIO.new( pdf.render ) returns ActiveRecord::RecordInvalid: Validation failed: File You are not allowed to upload nil files, allowed types: jpg, jpeg, gif, png, pdf, doc, docx, xls, xlsx

fileString = File.open( pdf.render ) returns ArgumentError: string contains null byte

....and so on.

What am I missing? StringIO.new( pdf.render ) seems like it should work, but I'm unclear why its generating this error.


Solution

  • It turns out StringIO.new( pdf.render ) should indeed work.

    The problem I was having was that the filename was being set incorrectly and, despite following the advise below on Carrierwave's wiki, a bug elsewhere in the code meant that the filename was returning as an empty string. I'd overlooked this an assumed that something else was needed

    https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Upload-from-a-string-in-Rails-3

    my code ended up looking like this

    def perform
      s = StringIO.new(pdf.render)
    
      def s.original_filename; "my file name"; end
    
      document  = Document.new(
        object_id: @object.id
      )
    
      document.file = s
    
      document.save!
    end