Rails, Active Storage, MiniMagick - конвертируйте страницы PDF в изображения (.png, .jpg, et c.) И загружайте в активное хранилище - PullRequest
2 голосов
/ 26 мая 2020

Я использую направляющие, активное хранилище и Minimagick, чтобы попытаться преобразовать многостраничный PDF-файл в отдельные изображения PNG, а затем сохранить изображения в активном хранилище.

Мне удалось успешно загрузить PDF-файл и конвертировать страницы в изображения PNG с помощью MiniMagick. Однако мне удалось получить Minimagi c только для хранения изображений png в каталоге app / assets / images (в отличие от сохранения изображений png как вложения активного хранилища).

Кто-нибудь знает, как заставить MiniMagick хранить изображения с активным хранилищем?

Вот код, который я использую:

# Doc model with active storage attachments
class Doc < ApplicationRecord
  has_one_attached :pdf         #PDF file to upload
  has_many_attached :pdf_pages  #PNG image for each page of the PDF
end 

# PrepareDocs controller manages saving a PNG image of each PDF page. 
# NOTE: at this point, the PDF has already been attached to the @doc instance. 
# Now we are trying to create the PNG images of each page of the PDF
class PrepareDocsController < ApplicationController
  def show
    # Load @doc
    @doc = Doc.find(params[:id])
    # Get PDF from @doc
    mini_magic_pdf = MiniMagick::Image.open(ActiveStorage::Blob.service.send(:path_for, @doc.pdf.key))

    # Save first page of doc.pdf as a PNG (later I will update this to convert every page to a PNG, but I'm starting on page 1 for now)
    MiniMagick::Tool::Convert.new do |convert|
      # Prepare formatting for PNG image
      convert.background "white"
      convert.flatten
      convert.density 150
      convert.quality 100
      # Save PNG Image - This successfully creates a PNG of the first page, but I don't want to store it in my assets.
      convert << mini_magic_pdf.pages.first.path
      convert << "app/assets/images/page_1.png"   # This probably needs to be changed so that we are not storing in the app/assets/imdages directory
      # [??? insert additional code to attach the png image to @doc.pdf_pages using active storage instead of to the images directory?]
      # NOTE: if I try to access the newly created png in my views, I get "incorrect signature" OR "asset not available in asset pipeline" errors
    end

  end
end

Любая помощь будет принята с благодарностью! Спасибо!

ОБНОВЛЕНИЕ: Я заставил его работать, и вот окончательный код:

# Doc model with active storage attachments
class Doc < ApplicationRecord
  has_one_attached :pdf         #PDF file to upload
  has_many_attached :pdf_pages  #PNG image for each page of the PDF
end 

# PrepareDocs controller manages saving a PNG image of each PDF page. 
# NOTE: at this point, the PDF has already been attached to the @doc instance. 
# Now we are trying to create the PNG images of each page of the PDF
class PrepareDocsController < ApplicationController
  def show
    # Load @doc
    @doc = Doc.find(params[:id])
    # path to current pdf (ie @doc.upoad)
    pdf_path = ActiveStorage::Blob.service.path_for(@doc.pdf.key)
    # set minimagick image wrapper for pdf stored in @doc.uplaod
    magick = MiniMagick::Image.open(pdf_path)

    # run repeat block to save each page as an individual image
    magick.pages.each_with_index do |page, index|
      # set file name to "page_N"
      file_name = "page_#{(index+1).to_s}"
      # set path for tempfile that you are about to create (using rails post ruby 2.5 approach. Pre 2.5 ruby uses make_tmpname; post 2.5 ruby uses create; I like rails post 2.5 version)
      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
      # attach using active storage - NOTE: this needs to be done AFTER the convert block
      @doc.pdf_pages.attach(io: File.open(converted_file_path), filename: file_name, content_type: "image/png")
      # remove tempfile
      FileUtils.rm(converted_file_path)
    end
  end 
end    

ПРИМЕЧАНИЕ. Я нашел 3 разных метода для создания временных файлов ( см. Здесь ) в зависимости от версии ruby и / или предпочтений:

      # pre ruby 2.5 
      converted_file_path = File.join(Dir.tmpdir, Dir::Tmpname.make_tempname(["page_1", ".png"], nil))
      # ruby 2.5 and later
      converted_file_path = File.join(Dir.tmpdir, Dir::Tmpname.create(["page_1", ".png"]) {} )
      # rails version of pre ruby 2.5 
      converted_file_path = File.join(Dir.tmpdir, "#{file_name}-#{Time.now.strftime("%Y%m%d")}-#{$$}-#{rand(0x100000000).to_s(36)}-.png")

1 Ответ

1 голос
/ 26 мая 2020

app/assets - это каталог для ваших источников ресурсов, изображения оттуда копируются в public/assets во время компиляции ресурсов (обычно выполняется при развертывании), поэтому для доступа к созданным изображениям - сохраните их где-нибудь в public, например, public/converted/page_1.png (у него будет путь /converted/page_1.png)

MiniMagick - это просто оболочка для утилит командной строки ImageMagick, поэтому вам нужно сохранить результат во временном месте перед загрузкой в ​​activestorage, а затем использовать что-то вроде :

converted_file_path = File.join(Dir.tmpdir, Dir::Tmpname.make_tmpname(["prefix-", ".png"], nil))
... # do convertation
@doc.pdf_pages.attach(io: File.open(converted_file_path), filename: 'page1.png')
FileUtils.rm(converted_file_path)
...