Hello Folks!
In this post I am going to demonstrate that how to upload PDF file into Rails using Active Storage and create image previews of pages of uploaded PDF. I am going to explain everything required to achieve the task into this post but I would like you to prefer my following blog posts because both will give you idea of adding bootstrap and active storage into rails app respectively.
How to add bootstrap 4 to your Rails 6 application?
Uploading Attachments in Rails Using Active Storage
However this is not nacessory though, because I will mainly cover the creating previews of PDF images part and at the end of this blog post I will provide the repository url for the work we have done in this post.
So enough talking and let's start with the task now. Just go through following steps:
1. Create a new rails app
Here I am creating a new rails application but please read the post carefully and you can apply the main feature in your existing application as well.
rails new pdf-preview-demo
cd pdf-preview-demo
bundle install
2. Create a model for uploading PDF
Here at this stage I am creating a model with name document, but I am just taking this for an example, you can choose any name for your resource. You just need to do few things in your model class.
Run following command into your terminal:
rails g scaffold document title:string description:text
3. Adding Active storage to application
Since Active Storage also require active_storage_blobs and active_storage_attachments tables to store attachments so we also need to create these tables as well. You need to run following command for getting migration for both tables:
rails active_storage:install
4. Run Migration
Now Run following commands to get all tables into your schema:
rails db:migrate
5. Add attachments attributes into model
Now we need attachments attributes for our model class. Since we are going to create preview of uploaded PDF pages, we require two attachment fields. Add both fields into your model. Open app/models/document.rb
and following lines into it:
# This is for uploading PDF
has_one_attached :doc_file
# This is uploading inages of pages of PDF
has_many_attached :doc_file_pages
So far at this stage your model should look like as below:
class Document < ApplicationRecord
validates :title, :description, presence: true
# Active storage attachments
has_one_attached :doc_file
has_many_attached :doc_file_pages
end
6. Permitting doc_file as params
Open your app/controllers/documents_controller.rb
and replace following method
def document_params
params.require(:document).permit(:title, :description)
end
with following:
def document_params
params.require(:document).permit(:title, :description, :doc_file)
end
7. Add doc_file field to form
Now since active storage attachment attributes will not be added to the table of model class, we need to add this into form manually. Add following snippet into app/views/documents/_form.html.erb
, before submit button
<%= form.label :upload_your_pdf_file_here %>
<%= form.file_field :doc_file, required: true %>
8. Add Minimagic gem to Gemfile
Add following line to bottom of your Gemfile and Run bundle install:
gem 'mini_magick', '>= 4.9.5'
now run
bundle install
9. Add method to convert pdf pages into images into model
Open app/models/document.rb
and add following method into it:
def convert_pdf_to_image
document = self
# path to current doc_file
doc_path = ActiveStorage::Blob.service.send(:path_for, document.doc_file.key)
# set minimagick image wrapper for pdf stored in @doc.uplaod
magick = MiniMagick::Image.open(doc_path)
magick.pages.each_with_index do |page, index|
file_name = "page_#{(index+1).to_s}"
# set path for tempfile that you are about to create
converted_file_path = File.join(Dir.tmpdir, "#{file_name}-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-.png")
# create png and save to tempfile path
MiniMagick::Tool::Convert.new do |convert|
# prep format
convert.background "white"
convert.flatten
convert.density 300
convert.quality 100
# add page to be converted,./
convert << page.path
# add path of page to be converted
convert << converted_file_path
end
document.doc_file_pages.attach(io: File.open(converted_file_path), filename: file_name, content_type: "image/png")
end
end
10. Call convert_pdf_to_image method after saving document
Since now we have method for converting pdf pages into images into our model class we need to call this method into controller right after saving the document. Open your app/controllers/documents_controller.rb
and replace create action into it by following method:
def create
@document = Document.new(document_params)
respond_to do |format|
if @document.save
@document.convert_pdf_to_image
format.html { redirect_to @document, notice: "Document was successfully created." }
format.json { render :show, status: :created, location: @document }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @document.errors, status: :unprocessable_entity }
end
end
end
And you done with all steps here.
Now give it a try. Open http://localhost:3000/documents/new and Create a document. Upload a PDF file of minimum of 2 pages (you can add single page PDF as well but to test previews in better way, I would recommend to upload a multipage PDF).
11. Display PDF pages as images
Now since you have upload your document and created its preview, you will definitely like to see the images. No worries, just open your app/views/documents/show.html.erb
and paste following snippet into it:
<% if @document.doc_file_pages.attached? %>
<% @document.doc_file_pages.each do |doc_page| %>
<%= image_tag doc_page, style: "width:500px;height:500px;" %>
<% end %>
<% end %>
That's it! We are done. Now you can customise this according to your need. But the main challenge for creating previews of PDF has been solved.
WARNING: Minimagic Auth Error While Creating Preview
You might get following error while creating preview images of pdf pages.
MiniMagick::Invalid (`identify /tmp/mini_magick20210515-8656-tjz3ib` failed with error:
identify-im6.q16: not authorized `/tmp/mini_magick20210515-8656-tjz3ib' @ error/constitute.c/ReadImage/412.
):
This error usually occurs if you use Minimagic first time on your system or try to create tmp files using Minimagic
Don't Worry.
Do following things:
Run following command on terminal:
sudo subl /etc/ImageMagick-6/policy.xml
and find following line into the file opened:
and replace with following line:
Now you done completely.
You can find more organised codebase for this blog post here: https://github.com/RaviSys/pdf-preview-demo.