I generate a PDF file for an invoice in a background job and I want to attach it to the invoice. I use Carrierwave for file uploads but here I do not upload it from the UI. I would like to be able to attach the file without saving it on disk.
invoice.rb
mount_uploader :file, InvoiceFileUploader
background job
class GeneratePdfJob < ApplicationJob
queue_as :default
def perform(invoice)
pdf = InvoiceServices::PdfGenerator.new(invoice)
file_name = [invoice.number.gsub('/','-'), invoice.due_date.to_s, SecureRandom.urlsafe_base64].join('-') + '.pdf'
pdf.render_file(file_name)
file = File.new(file_name)
invoice.file = file
File.delete(file_name)
end
end
So right now I call render_file
method to actually create the file but this file gets saved in the root folder of my application so I need to delete it afterwards. Is there a better way? Is there a way to attach the file without actually saving it on disk?
what you are trying to archive is really impressive. Thanks for the idea. this will reduce lots of disk IO-related issues in PDF generation.
1st: Renders the PDF document to a string
instead of render_file method use Prawn::Document#render
method which returns string representation of the PDF.
2nd: use that string to upload to carrier wave without any tempory file.
# define class that extends IO with methods that are required by carrierwave
class CarrierStringIO < StringIO
def original_filename
"invoice.pdf"
end
def content_type
"application/pdf"
end
end
class InvoiceFileUploader < CarrierWave::Uploader::Base
def filename
[model.number.gsub('/','-'), model.due_date.to_s, SecureRandom.urlsafe_base64].join('-') + '.pdf'
end
end
class Invoice
mount_uploader :file, InvoiceFileUploader
def pdf_data=(data)
self.file = CarrierStringIO.new(data)
end
end
class GeneratePdfJob < ApplicationJob
queue_as :default
def perform(invoice)
pdf = InvoiceServices::PdfGenerator.new(invoice)
invoice.pdf_data = pdf.render
end
end