Rails render multiple pdf files to a folder - ruby-on-rails

I have a controller action that renders a pdf for download.
I want to render multiple pdfs to a tmp folder ( then zip them for download )
I can generate the pdfs and present to the user but I can't figure out how to create a folder to store them in.
I'm using prawn. It has the render_file method to save it to the filesystem but I don't know what directory it is or if other users could save their pdf's to the same folder, so I need to create a uniques folder for each user then save the pdf's there.
How can I do this?
my controller action is currently
def showpdf
respond_to do |format|
format.html
format.pdf do
#items.each do |pdf|
pdf = Prawn::Document.new(page_size: "A4",margin: [0,0,0,0])
# pdf creation stuff...
# this was used previously to render one pdf to the browser
# but I need to save multiple pdf's
#send_data pdf.render, filename: 'report.pdf', type: 'application/pdf'
end
end
end

You will need to store all files into tmp/your-folder folder, something like this
require 'prawn'
#items.each do |item|
pdf = Prawn::Document.new
pdf.text("Lets Zip All.")
pdf.render_file('tmp/your-folder/#{item.id}.pdf')
end
and then simply use https://github.com/rubyzip/rubyzip to zip the your-folder.
require 'zip'
folder = "tmp/your-folder/"
zipfile_name = "tmp/archive.zip"
input_filenames = Dir.entries("tmp/your-folder/").select {|f| !File.directory? f}
Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
input_filenames.each do |filename|
zipfile.add(filename, folder + '/' + filename)
end
zipfile.get_output_stream("myFile") { |os| os.write "myFile contains just this" }
end
Simply send the file to user. But if the PDF contains a lot of data move them to delayed jobs.
Hope this makes sense but if not please hit reply.

Related

WickedPDF generating blank pdfs when attempting to render multiple pdfs

I am using WickedPDF in a Rails 6 project to generate PDFs from HTML, and then combine them with prefilled PDF forms. The WickedPDF generated PDFs are split into two sections, each of which must be combined with a form. The forms must appear at the beginning of their respective sections.
I have attempted to generate two PDFs with WickedPDF and then combine them with the prefilled forms using combine_pdf in the appropriate order.
The render_ar_docs and render_gl_docs both work as expected when I visit their routes individually: they generate and save the expected PDF. However, when I call them sequentially in the print_complete_docs action, the resulting PDFs are a single blank page.
How can I generate multiple PDFs from a single action?
Thank you for your help.
def print_complete_docs
#policy.fill_and_save_acord28
#policy.fill_and_save_acord25
render_ar_docs
render_gl_docs
pdf = CombinePDF.new
pdf << CombinePDF.load("tmp/acord28.pdf")
pdf << CombinePDF.load("tmp/ar_docs.pdf")
pdf << CombinePDF.load("tmp/acord25.pdf")
pdf << CombinePDF.load("tmp/gl_docs.pdf")
pdf.save "tmp/complete_docs.pdf"
send_file("#{Rails.root}/tmp/complete_docs.pdf", filename: "tmp/#{#policy.legal_vesting} Complete Docs.pdf", type: 'application/pdf')
end
def render_ar_docs
render pdf: 'ar_docs',
layout: 'document',
save_to_file: Rails.root.join('tmp', "ar_docs.pdf"),
save_only: true
end
def render_gl_docs
render pdf: 'gl_docs',
layout: 'document',
save_to_file: Rails.root.join('tmp', "gl_docs.pdf"),
save_only: true
end
My issue seems to be related to trying to render multiple times in a single request, which Rails does not allow. Instead I should have used WickedPDF's pdf_from_string method.
My new (over verbose and un-refactored, but functional) method looks like this:
def print_complete_docs
#policy.fill_and_save_acord28
#policy.fill_and_save_acord25
render_ar_docs
render_gl_docs
pdf = CombinePDF.new
pdf << CombinePDF.load("tmp/acord28.pdf")
pdf << CombinePDF.load("tmp/ar_docs.pdf")
pdf << CombinePDF.load("tmp/acord25.pdf")
pdf << CombinePDF.load("tmp/gl_docs.pdf")
pdf.save "tmp/complete_docs.pdf"
send_file("#{Rails.root}/tmp/complete_docs.pdf", filename: "tmp/#{#policy.legal_vesting} Complete Docs.pdf", type: 'application/pdf')
end
def render_ar_docs
ar_docs = WickedPdf.new.pdf_from_string(
render_to_string(
'policies/ar_docs.html.erb',
layout:'document.html.erb',
locals: { policy: #policy }
),
layout: 'document.html.erb'
)
save_path = Rails.root.join('tmp','ar_docs.pdf')
File.open(save_path, 'wb') do |file|
file << ar_docs
end
end
def render_gl_docs
gl_docs = WickedPdf.new.pdf_from_string(
render_to_string(
'policies/gl_docs.html.erb',
layout:'document.html.erb',
locals: { policy: #policy }
),
layout: 'document.html.erb'
)
save_path = Rails.root.join('tmp','gl_docs.pdf')
File.open(save_path, 'wb') do |file|
file << gl_docs
end
end
PS: Thanks to #Unixmonkey for trying to help, and for his contributions to WickedPDF!

How to save a Prawn generated PDF to the public folder?

I'm using Prawn gem to generate PDFs In my application...
app/controllers/orders.rb
class OrdersController < ApplicationController
...
def show
respond_to do |format|
format.html
format.pdf do
require "prawn/measurement_extensions"
...
#render pdf document
send_data pdf.render,
filename: "order_#{#order.id}.pdf",
type: 'application/pdf',
disposition: 'inline'
end
end
end
...
end
And It's working fine for displaying, But My questions are..
How to -Automatically- save those generated pdfs in the public folder (folder for each day) after a successful order creation? I've tried searching Prawn Documentation But I've found nothing.
How to show orders in only pdf format? I've Tried to Comment the format.html line but It didn't work
When you call pdf.render and send it to the client with send_data, you're actually dumping the contents of the pdf over the wire. Instead of that, you could dump the result of pdf.render in a file with File.new and use send_file in the controller. Check the documentation for File. Alternatively, you could attach the generated pdf to the specific order using something like Paperclip.
If you generate the pdf as a file in the server, you should use send_file instead of send_data. Read more about this in the guides.
You can use Prawn's render_file method to save the generated PDF to a file:
pdf.render_file(Rails.root.join('public', "order_#{#order.id}.pdf"))
See documentation: https://prawnpdf.org/docs/0.11.1/Prawn/Document.html

Google Ruby API Client Create File Response

I am currently working on a Rails project where a file gets uploaded to Drive. I am able to get files uploaded to Drive however am wondering how to get a response that contains the file ID, link, etc. Do I need to use list for this? Any help would be greatly appreciated.
def create
#essay = Essay.new(params.require(:essay).permit(:course_name))
# Uploaded File
uploaded_io = params[:essay][:essay_draft]
# Save to a temporary folder
Tempfile.open(uploaded_io.original_filename, Rails.root.join('private', 'tmp')) do |f|
# Write using UTF-8 encoding
f.write(uploaded_io.read.force_encoding("UTF-8"))
# Close the file
f.close
# Gotta unlink to delete the temp file
f.unlink
end
# Set Metadata to be sent to Google Drive
file_metadata = {
name: uploaded_io.original_filename,
mime_type: 'application/vnd.google-apps.document'
}
# Call method which will upload the actual file to Drive
#drive.create_file(file_metadata,
fields: 'id',
upload_source: uploaded_io.path,
content_type: 'text/doc')
if #essay.save
redirect_to #essay
else
render :new
end
end
This is what I have in my create method.
def create
#essay = Essay.new(params.require(:essay).permit(:course_name))
# Uploaded File
uploaded_io = params[:essay][:essay_draft]
# Set Metadata to be sent to Google Drive
file_metadata = {
name: uploaded_io.original_filename,
mime_type: 'application/vnd.google-apps.document'
}
# Call method which will upload the actual file to Drive
#file = #drive.create_file(file_metadata,
fields: 'id, web_view_link',
upload_source: uploaded_io.path,
content_type: 'text/doc')
if #essay.save
render :show
else
render :new
end
end
I was then able to put the following into my view:
<%= #file.id %>
<%= #file.web_view_link %>

Rails send multiple files to browser using send_data or send_file

I am trying to send multiple files to the browser. I cant call send_data for every record like in the code below because i get a double render error. According to this post i need to create the files and zip them so i can send them in one request.
#records.each do |r|
ActiveRecord::Base.include_root_in_json = true
#json = r.to_json
a = ActiveSupport::MessageEncryptor.new(Rails.application.config.secret_token)
#json_encrypted = a.encrypt_and_sign(#json)
send_data #json_encrypted, :filename => "#{r.name}.json" }
end
I am creating an array of hashes with the #json_encrypted and the file_name for each record. My question is how can i create a file for every record and then bundle them into a zip file to then pass that zip file to send_file. Or better yet have multiple file download dialogs pop up on the screen. One for each file.
file_data = []
#records.each do |r|
ActiveRecord::Base.include_root_in_json = true
#json = r.to_json
a = ActiveSupport::MessageEncryptor.new(Rails.application.config.secret_token)
#json_encrypted = a.encrypt_and_sign(#json)
file_data << { json_encrypted: #json_encrypted, filename: "#{r.name}.json" }
end
So the issue i was having is that send_file does not respond to an ajax call which is how i was posting to that action. I got rid of the ajax, and am sending the necessary data though a hidden_field_tag and submitting it with jquery. The code below creates files for the data, zips them, and passes the zip file to send_data.
file_data.each do |hash|
hash.each do |key, value|
if key == :json_encrypted
#data = value
elsif key == :filename
#file_name = value
end
end
name = "#{Rails.root}/export_multiple/#{#file_name}"
File.open(name, "w+") {|f| f.write(#data)}
`zip -r export_selected "export_multiple/#{#file_name}"`
send_file "#{Rails.root}/export_selected.zip", type: "application/zip", disposition: 'attachment'
end

Allowing client to download files from ftp rails

I'm developping a web application in rails 4 and I'm currenty faced with a tiny issue.
I want to make the users of the website to be able to download files from a ftp by clicking on a link. I've decided to go on this:
def download
#item=Item.find(params[:id])
#item.dl_count += 1
#item.save
url = #item.file_url.to_s
redirect_to url and return
end
And, very basically, this in my view:
<%= link_to 'DL', controller: "items", action: "download"%>
However, I'm not quite satisfied by this, as it generates a few mistake like the fact that clicking the link create two GET methods, one responding by 403 Forbidden and the next with a 302 found...
Do you have any idea about how I could improve this?
In Rails you should do:
def download
#item=Item.find(params[:id])
#item.dl_count += 1
#item.save
url = #item.file_url.to_s
send_file url, type: 'image/jpeg', disposition: 'inline'
end
Take a look for more information http://apidock.com/rails/ActionController/DataStreaming/send_file
Note that send_file can send only from local file system.
If you need get file from remote source (should be secure location) like http://example.com/apps/uploads/tfm.zip and avoid store this file in server memory, you can first save file in #{RAILS_ROOT}/tmp/ or system /tmp and then send_file
data = open(url)
filename = "#{RAILS_ROOT}/tmp/my_temp_file"
File.open(filename, 'w') do |f|
f.write data.read
end
send_file filename, ...options...
If Rails can`t read file, you should check file permission

Resources