如何处理file_as_string(由Prawn 生成)以便它被Carrierwave 接受?
How to handle a file_as_string (generated by Prawn) so that it is accepted by Carrierwave?
我正在使用 Prawn 从 Rails 应用程序的控制器生成 PDF,
...
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
这很好用,但我现在想将 GenerateReportPdf 移动到后台任务中,并将生成的对象传递给 Carrierwave 以直接上传到 S3。
工人长这样
def perform
pdf = GenerateReportPdf.new(@object)
fileString = ???????
document = Document.new(
object_id: @object.id,
file: fileString )
# file is field used by Carrierwave
end
如何处理Prawn(?????
)返回的对象,以确保它是Carrierwave可以读取的格式。
fileString = pdf.render_file 'filename'
将对象写入应用程序的根目录。因为我在 Heroku 上,所以这是不可能的。
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
.....等等。
我错过了什么? StringIO.new( pdf.render )
似乎应该可以,但我不清楚为什么会产生此错误。
您想创建一个临时文件(这在 Heroku 上没问题,只要您不希望它在请求中持续存在即可)。
def perform
# Create instance of your Carrierwave Uploader
uploader = MyUploader.new
# Generate your PDF
pdf = GenerateReportPdf.new(@object)
# Create a tempfile
tmpfile = Tempfile.new("my_filename")
# set to binary mode to avoid UTF-8 conversion errors
tmpfile.binmode
# Use render to write the file contents
tmpfile.write pdf.render
# Upload the tempfile with your Carrierwave uploader
uploader.store! tmpfile
# Close the tempfile and delete it
tmpfile.close
tmpfile.unlink
end
事实证明 StringIO.new( pdf.render )
应该确实有效。
我遇到的问题是文件名设置不正确,尽管遵循了 Carrierwave 维基上的以下建议,代码中其他地方的错误意味着文件名作为空字符串返回。我忽略了这一点并假设还需要其他东西
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Upload-from-a-string-in-Rails-3
我的代码最终看起来像这样
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
这里有一种方法可以像 Andy Harvey 提到的那样使用 StringIO,但无需向 StringIO 实例的特征类添加方法。
class VirtualFile < StringIO
attr_accessor :original_filename
def initialize(string, original_filename)
@original_filename = original_filename
super(string)
end
end
def perform
pdf_string = GenerateReportPdf.new(@object)
file = VirtualFile.new(pdf_string, 'filename.pdf')
document = Document.new(object_id: @object.id, file: file)
end
这花了我几天时间,关键是调用 render_file
控制文件路径,这样你就可以跟踪文件,像这样:
在我的一个模型中,例如:Policy
我有一个文档列表,这只是更新与载波相关的模型的方法,例如:PolicyDocument < ApplicationRecord mount_uploader :pdf_file, PdfDocumentUploader
def upload_pdf_document_file_to_s3_bucket(document_type, filepath)
policy_document = self.policy_documents.where(policy_document_type: document_type)
.where(status: 'processing')
.where(pdf_file: nil).last
policy_document.pdf_file = File.open(file_path, "r")
policy_document.status = 's3_uploaded'
policy_document.save(validate:false)
policy_document
rescue => e
policy_document.status = 's3_uploaded_failed'
policy_document.save(validate:false)
Rails.logger.error "Error uploading policy documents: #{e.inspect}"
end
end
在我的 Prawn PDF 文件生成器之一中,例如:PolicyPdfDocumentX
在这里请注意我如何渲染文件并返回文件路径,以便我可以从工作对象本身获取
def generate_prawn_pdf_document
Prawn::Document.new do |pdf|
pdf.draw_text "Hello World PDF File", size: 8, at: [370, 462]
pdf.start_new_page
pdf.image Rails.root.join('app', 'assets', 'images', 'hello-world.png'), width: 550
end
end
def generate_tmp_file(filename)
file_path = File.join(Rails.root, "tmp/pdfs", filename)
self.generate_prawn_pdf_document.render_file(file_path)
return filepath
end
在 "global" Worker 中创建文件并将它们上传到 s3 存储桶中,例如:PolicyDocumentGeneratorWorker
def perform(filename, document_type, policy)
#here we create the instance of the prawn pdf generator class
pdf_generator_class = document_type.constantize.new
#here we are creating the file, but also `returning the filepath`
file_path = pdf_generator_class.generate_tmp_file(filename)
#here we are simply updating the model with the new file created
policy.upload_pdf_document_file_to_s3_bucket(document_type, file_path)
end
最后怎么测试,运行 rails c
和:
the_policy = Policies.where....
PolicyDocumentGeneratorWorker.new.perform('report_x.pdf', 'PolicyPdfDocumentX',the_policy)
注意: 我使用元编程以防我们有多个不同的文件生成器,constantize.new
只是创建新的 prawn pdf 文档生成器instance so 类似于 PolicyPdfDocument.new
这样我们只能有一个 pdf doc generator worker class 可以处理 all
你的 prawn pdf 文档所以例如如果你需要一个新文档你可以只是 PolicyDocumentGeneratorWorker.new.perform('report_y.pdf', 'PolicyPdfDocumentY',the_policy)
:D
希望这有助于节省一些时间
我正在使用 Prawn 从 Rails 应用程序的控制器生成 PDF,
...
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
这很好用,但我现在想将 GenerateReportPdf 移动到后台任务中,并将生成的对象传递给 Carrierwave 以直接上传到 S3。
工人长这样
def perform
pdf = GenerateReportPdf.new(@object)
fileString = ???????
document = Document.new(
object_id: @object.id,
file: fileString )
# file is field used by Carrierwave
end
如何处理Prawn(?????
)返回的对象,以确保它是Carrierwave可以读取的格式。
fileString = pdf.render_file 'filename'
将对象写入应用程序的根目录。因为我在 Heroku 上,所以这是不可能的。
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
.....等等。
我错过了什么? StringIO.new( pdf.render )
似乎应该可以,但我不清楚为什么会产生此错误。
您想创建一个临时文件(这在 Heroku 上没问题,只要您不希望它在请求中持续存在即可)。
def perform
# Create instance of your Carrierwave Uploader
uploader = MyUploader.new
# Generate your PDF
pdf = GenerateReportPdf.new(@object)
# Create a tempfile
tmpfile = Tempfile.new("my_filename")
# set to binary mode to avoid UTF-8 conversion errors
tmpfile.binmode
# Use render to write the file contents
tmpfile.write pdf.render
# Upload the tempfile with your Carrierwave uploader
uploader.store! tmpfile
# Close the tempfile and delete it
tmpfile.close
tmpfile.unlink
end
事实证明 StringIO.new( pdf.render )
应该确实有效。
我遇到的问题是文件名设置不正确,尽管遵循了 Carrierwave 维基上的以下建议,代码中其他地方的错误意味着文件名作为空字符串返回。我忽略了这一点并假设还需要其他东西
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Upload-from-a-string-in-Rails-3
我的代码最终看起来像这样
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
这里有一种方法可以像 Andy Harvey 提到的那样使用 StringIO,但无需向 StringIO 实例的特征类添加方法。
class VirtualFile < StringIO
attr_accessor :original_filename
def initialize(string, original_filename)
@original_filename = original_filename
super(string)
end
end
def perform
pdf_string = GenerateReportPdf.new(@object)
file = VirtualFile.new(pdf_string, 'filename.pdf')
document = Document.new(object_id: @object.id, file: file)
end
这花了我几天时间,关键是调用 render_file
控制文件路径,这样你就可以跟踪文件,像这样:
在我的一个模型中,例如:Policy
我有一个文档列表,这只是更新与载波相关的模型的方法,例如:PolicyDocument < ApplicationRecord mount_uploader :pdf_file, PdfDocumentUploader
def upload_pdf_document_file_to_s3_bucket(document_type, filepath)
policy_document = self.policy_documents.where(policy_document_type: document_type)
.where(status: 'processing')
.where(pdf_file: nil).last
policy_document.pdf_file = File.open(file_path, "r")
policy_document.status = 's3_uploaded'
policy_document.save(validate:false)
policy_document
rescue => e
policy_document.status = 's3_uploaded_failed'
policy_document.save(validate:false)
Rails.logger.error "Error uploading policy documents: #{e.inspect}"
end
end
在我的 Prawn PDF 文件生成器之一中,例如:PolicyPdfDocumentX
在这里请注意我如何渲染文件并返回文件路径,以便我可以从工作对象本身获取
def generate_prawn_pdf_document
Prawn::Document.new do |pdf|
pdf.draw_text "Hello World PDF File", size: 8, at: [370, 462]
pdf.start_new_page
pdf.image Rails.root.join('app', 'assets', 'images', 'hello-world.png'), width: 550
end
end
def generate_tmp_file(filename)
file_path = File.join(Rails.root, "tmp/pdfs", filename)
self.generate_prawn_pdf_document.render_file(file_path)
return filepath
end
在 "global" Worker 中创建文件并将它们上传到 s3 存储桶中,例如:PolicyDocumentGeneratorWorker
def perform(filename, document_type, policy)
#here we create the instance of the prawn pdf generator class
pdf_generator_class = document_type.constantize.new
#here we are creating the file, but also `returning the filepath`
file_path = pdf_generator_class.generate_tmp_file(filename)
#here we are simply updating the model with the new file created
policy.upload_pdf_document_file_to_s3_bucket(document_type, file_path)
end
最后怎么测试,运行 rails c
和:
the_policy = Policies.where....
PolicyDocumentGeneratorWorker.new.perform('report_x.pdf', 'PolicyPdfDocumentX',the_policy)
注意: 我使用元编程以防我们有多个不同的文件生成器,constantize.new
只是创建新的 prawn pdf 文档生成器instance so 类似于 PolicyPdfDocument.new
这样我们只能有一个 pdf doc generator worker class 可以处理 all
你的 prawn pdf 文档所以例如如果你需要一个新文档你可以只是 PolicyDocumentGeneratorWorker.new.perform('report_y.pdf', 'PolicyPdfDocumentY',the_policy)
:D
希望这有助于节省一些时间