Rails 5 pdf preview with Shrine Gem

Charles Smith

I am using Shrine Gem for image uploads and was wondering how, if possible, can I show a 1st page preview in my template like showing an image per-say. I am ok with using jQuery or other library. Below is my code for the file upload, including my Shrine initializer and uploader file.

view

...
<div class="col-md-4 upload-block">
    <%= f.label :spec_sheet, 'Spec Sheet' %>
      <% if @product.spec_sheet.present? %>
        <div class="product-image">
          <%= image_tag(@product.spec_sheet_url(:thumb)) %>
            <div class="input-checkbox input-checkbox--switch">
              <input name="product[remove_spec_sheet]" type="hidden" value="0">
              <input id="checkbox-switch" type="checkbox" name="product[remove_spec_sheet]">
              <label for="checkbox-switch"></label>
            </div>
            <span>Remove Spec Sheet</span>
        </div>
        <% end %>
          <%= f.hidden_field :spec_sheet, value: @product.cached_spec_sheet_data %>
            <%= f.file_field :spec_sheet %>
  </div>
...

initializer

require 'shrine'
require 'shrine/storage/file_system'

Shrine.storages = {
    cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'),
    store: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/store'),
}

Shrine.plugin :activerecord
Shrine.plugin :remove_attachment
Shrine.plugin :delete_raw
Shrine.plugin :cached_attachment_data # for forms 

uploader

require 'image_processing/mini_magick'
class ImageUploader < Shrine
  MAX_IMAGE_SIZE_MB = 5
  include ImageProcessing::MiniMagick

  plugin :determine_mime_type
  plugin :remove_attachment
  plugin :store_dimensions
  plugin :validation_helpers
  plugin :processing
  plugin :versions
  plugin(:default_url) { |_|  '/img/preview-not-available.jpg' }

  Attacher.validate do
    validate_max_size MAX_IMAGE_SIZE_MB.megabytes, message: "is too large (max is #{MAX_IMAGE_SIZE_MB} MB)"
    validate_mime_type_inclusion %w[image/jpeg image/jpg image/png image/gif]
  end

  process(:store) do |io|
    original = io.download

    size_1500 = resize_to_limit!(original, 1500, 600)
    size_500 = resize_to_limit(size_1500,  500, 500)
    size_300 = resize_to_limit(size_500,  300, 300)

    {original: size_1500, medium: size_500, thumb: size_300 }
  end
end
janko-m

If you want to show PDF previews, you'll need to generate them on the server side. In that case it's best to use direct uploads (see the demo for an example of a client side implementation).

You can then generate PDFs preview on direct upload:

# config/initializers/shrine.rb
Shrine.plugin :determine_mime_type

# app/models/image_uploader.rb
class ImageUploader < Shrine
  plugin :processing
  plugin :versions

  process(:upload) do |io, context|
    if Shrine.determine_mime_type(io) == "application/pdf"
      preview = Tempfile.new(["shrine-pdf-preview", ".pdf"], binmode: true)
      begin
        IO.popen *%W[mutool draw -F png -o - #{io.path} 1], "rb" do |command|
          IO.copy_stream(command, preview)
        end
      rescue Errno::ENOENT
        fail "mutool is not installed"
      end

      preview.open # flush & rewind
    end

    versions = { original: io }
    versions[:preview] = preview if preview && preview.size > 0
    versions
  end
end
  • the upload_endpoint sets the :upload processing action, so that's the meaning of process(:upload)
  • we use rb for IO.popen so that Ruby uses binary encoding, which is more safe and cross-platform
  • Kernel#spawn and any methods that use spawn (system, IO.popen etc.) will raise Errno::ENOENT when shell command was not found
  • we use *%W[] instead of just "" so that Ruby avoids the shell (and with that any possible shell escaping issues) and passes the command directly to the OS
  • we check whether the preview file is nonempty because it will be empty in case the mutool command failed (in this case we probably want to fall back to not displaying a preview)

The result of the POST request to the upload_endpoint will now contain the uploaded file ID of the preview, which you can use to generate the URL to the preview. In your case that would be "/uploads/cache" + id.

Note that this means you'll have to slightly modify the processing code that is called when cached file is promoted to permanent storage. In the process(:store) do |io, context| block the io will now be a hash of versions, so you can access the original cached file via io[:original]. And be sure to also include the preview file in the result of that block, as you'll likely want to keep it.

process(:store) do |io, context|
  original = io[:original].download

  # processing...

  versions = io.dup
  versions[:small] = small
  versions[:medium] = medium
  # ...
  versions
end

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Rails 5 + Shrine multiple files upload

Gem wicked pdf - CSS doesn't work with rails 5

Annotate Gem ruby on rails 5

Migration to Rails 5, rails gem issue

Rails Prawn Pdf gem suggestions needed

Upload to Rails Shrine from NativeScript

Blur images with Shrine and image_processing gem

Rails 5: javascript preview and remove image, align horizontally the preview pics

Rails 5 - Shrine issue: "undefined method `cached_image_data' for nil:NilClass"

Ruby on Rails: Prawn Gem Generates PDF, but PDF Cannot Display

Rails 5: Kaminari gem with ajax setup

Rails 5 CarrierWave Gem Works in Production But Not In Development

LoadError: Could not load the 'listen' gem (Rails 5)

Rails 5 - view changes using Audited gem

Rails 5 - Geocoder Gem, Search with Multiple Parameters

Rails and Shrine create model record with attachment

Shrine with Rails multiple polymorphic image uploads

Rails Multiple Uploads with Shrine and nested attachments

Rails 5 + font-awesome-rails gem - only boxes show

Insert QR code into PDF using gem receipts in Ruby on Rails

Shrine gem - how to delete uploaded images from s3

Rails 5, Simple Fields For with Cocoon gem for nested resources

Rails 5 - Acts as Taggable On gem - defined tag lists

Bundler install - could not find compatible versions for gem "actionpack" - Rails 5

Full Rails 5 routes with Ancestry Gem nested tree

Changes in local gem does not show in the application in Rails 5

Rails 5: Array values in the parameter to `Gem.paths=` are deprecated

Rails 5: db:migrate fails with globalize gem PG::UndefinedTable

Rails 5 'full_gem_path' : uninitialized constant Bundler