Convert Pdfs to Zip in Rails

Convert Pdfs to Zip in Rails

·

3 min read

There are many scenarios where you might need a zip file in Rails if the size of the files is big. Here's a setup of the Gemfile and Controller.

There's a gem in Ruby called 'rubyzip' that converts large files to zip file. You need to add the zip gem to your gemfile, or you can install it by yourself.

     gem 'rubyzip', '>= 1.0.0'
     gem 'zip-zip'
     gem install rubyzip

If you have issues with any third-party gems that require an old version of rubyzip, you can use this workaround. Then, in your controller, you need to add files to the zip folder and then send it to the end-user. In my case, I have some pdfs that I need to convert to a zip file. Create a method in the controller to add some code for the zip file.

Ex: My controller method is get_zip

First of all, we require a zip gem in our controller form. For that, we'll add require 'zip' in the first line of the controller. We need a folder to store pdfs. Usually, all these temporary files are stored in the 'tmp' folder of the project. So, we'll create a folder in the tmp directory that will store all the pdfs.

     folder = "#{Rails.root}/tmp/pdfs/suppliers"

Here, Rails.root = /home/ritu/MyProject

Thus, we joined '/tmp/pdfs/suppliers' to it. We don't need to create these files manually. We've Ruby's FileUtils method mkdir_p that creates all the folders that aren't available.

     FileUtils.mkdir_p(folder) unless File.exist?(folder)

It will create a suppliers directory within the pdfs directory. Now that the folders have built, we'll add pdfs to them. I am using Prawn gem for pdf.

    @suppliers = Supplier.all
    @start_date =   Date.today.prev_month.beginning_of_month
    @end_date = Date.today.prev_month.end_of_month

    @suppliers.each do |supplier|  
       @orders = supplier.orders.where("orders.created_at Between ? And ?",@start_date, @end_date)

       if @orders.present?
         pdf = SupplierStatementPdf.new(@orders, supplier, @start_date, @end_date, view_context)
         pdf.render_file("tmp/pdfs/suppliers/#{supplier.id}.pdf")
       end
    end

The code generates only those pdfs that have an order of the previous month.

pdf.render_file("tmp/pdfs/suppliers/#{supplier.id}.pdf")

This line renders all the pdfs to the folder that we just created, and adds pdfs with the supplier id as its name.

To convert these files to a zip file, there must be a zip file in the tmp directory.

  zipfile_name = File.join(Rails.root, "tmp", "supplier_statements.zip")

File.join joins the associated string to the file path. Here, supplier_statements.zip is the file where all the pdfs will be stored. We'll extract pdfs/supplier folders' each file and add them to an array.

    input_filenames = Dir.entries("tmp/pdfs/suppliers/").select {|f| !File.directory? f}
    if input_filenames.present?
      Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
        input_filenames.each do |filename|
          zipfile.add(filename, folder + '/' + filename)
        end
      end
      send_file File.join(File.join(Rails.root, "tmp"), "supplier_statements.zip")
    else
      flash[:alert] = "No Suppliers found for the statements"
      redirect_to root_path
    end

zipfile.add(filename, folder + '/' + filename) This line adds all the files/pdfs of the folder to the zip file.

At the end, send_file File.join(File.join(Rails.root, "tmp"), "supplier_statements.zip") this line makes our zip file a downloadable file.

We're done with zip creation. But, there's a small problem with the code. Guess what??

It'll work the first time, but then will throw an error like,

file already exists in the zip folder.

To deal with the exception, we'll add a line to delete the already created folder.

     FileUtils.rm_rf("#{Rails.root}/tmp/pdfs/suppliers")
     FileUtils.rm_rf("#{Rails.root}/tmp/supplier_statements.zip")

Add these two lines at the beginning of the method. And you're done!!