Rails、Active Storage、MiniMagick - 将 PDF 页面转换为图像(.png、.jpg 等)并上传到活动存储
Rails, Active Storage, MiniMagick - convert PDF pages to images (.png, .jpg, etc.) and upload to active storage
我正在使用 rails、活动存储和 minimagick 尝试将多页 PDF 转换为单独的 PNG 图像,然后使用活动存储存储图像。
我已经能够使用 MiniMagick 成功加载 PDF 并将页面转换为 png 图像。但是,我只能让 Minimagic 将 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 种不同的方法来创建临时文件 (see here),具体取决于 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")
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)
我正在使用 rails、活动存储和 minimagick 尝试将多页 PDF 转换为单独的 PNG 图像,然后使用活动存储存储图像。
我已经能够使用 MiniMagick 成功加载 PDF 并将页面转换为 png 图像。但是,我只能让 Minimagic 将 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 种不同的方法来创建临时文件 (see here),具体取决于 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")
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)