Carrierwave Fog get file extention - carrierwave

I am using CarrierWave and Fog. Need to access the extension of the file being uploaded to be able to check if it is an image or a video?
How do I go about this? I need to execute fix_exif_rotation if it is an image being updated and extension is in image_file_list.
class MediaUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :fog
def extension_white_list
%w(srt xml mid midi kar aac f4a f4b m4a mp3 oga.ogg opus ra wav bmp gif jpeg jpg jxr hdp wdp png svg svgz tif tiff wbmp webp jng 3gp 3gpp f4p f4v m4v mp4 mpeg mpg ogv mov webm flv mng asf asx wmv avi)
end
def fix_exif_rotation
manipulate! do |img|
img.tap(&:auto_orient)
end
end
image_file_list = %w(bmp gif jpeg jpg png tif tiff)
end

You can use process in your uploader like this:
process :fix_exif_rotation
Then in youre fix_exif_rotation method you can do the following:
if image_file_list.include? self.file.extension
// youre code here
end
Hope this is what youre looking for

Related

How do I preserve the transparent background when converting an SVG file to PNG with MiniMagick in a Ruby on Rails application?

I am trying to automatically convert SVG images to PNG with Minimagick. I have a Rails application where I need to automatically convert uploaded SVG files. The app runs on Heroku. These SVGs typically have a transparent background.
This is the code I am using:
class MyUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
version :email do
process :convert_to_png
def full_filename (for_file = model.logo.file)
"#{model.friendly_id}-#{model.id}-logo-email.png"
end
end
def convert_to_png
manipulate! do |img|
img = img.format 'png'
img = img.density 300
img = img.resize '800x400'
img = img.background 'none'
end
end
end
This creates a png image with width 800. However the property background 'none' is not applied. The original image has a transparent background, and the resulting converted image has a white background instead.
I have seen that Minimagick calls the mogrify command of Imagemagick, and I have found the command I have to call to create the image I need, but I have trouble converting it into a function for the uploader. Using the same parameters in the same sequence for the uploader doesn't work because it throws an error if the line img = img.format 'png' isn't called first.
mogrify -density 300 -background none -resize 400x200 -format png myfile.svg
Note: I don't use this library so all of this is gleaned from the docs and the source code. If these don't work for you I will remove the post it was just too long for a comment
Here are few suggestions:
Maybe try combine_options instead? "MiniMagick::Image#combine_options takes multiple options and from them builds one single command." which is more in line with your desired CLI call
def convert_to_png
manipulate! do |img|
img.combine_options do |i|
i.density 300
i.background 'none'
i.resize '800x400'
i.format 'png'
end
end
end
Another option is to touch the Metal and generate that exact command.
MiniMagick::Tool::Mogrify.new do |mogrify|
mogrify << "input.svg"
mogrify.density(300)
mogrify.background('none')
mogrify.resize('800x400')
mogrify.format('png')
mogrify << "output.png"
end
#=> `mogrify input.svg -density 300 -background none -resize 800x400 -format png output.png`

Rails ActiveStorage: Attach a Vips Image

I'm struggling to attach a Vips:Image object to an ActiveStorage object.
I use Vips to compress a PNG image. I'm then looking to save this compressed PNG version into a second ActiveStorage attachment. The code is failing when attempting to attach. All I get is: Process finished with exit code -1073741819 (0xC0000005)
# compress the image and save the compressed version of the file as a PNG
# both img and img_to_compress are active storage attachments
def compress_charlie(img, img_to_compress)
# load the image as a Vips Image object
vips_img = Vips::Image.new_from_buffer (URI.open(img.url) { |f| f.read }), ''
# do compression, etc ... not bothering to show this code as it has no impact on the issue I have
# save the compressed png
img_to_compress.attach(
io: StringIO.new(vips_img .write_to_buffer('.png')),
filename: img.filename
)
end
Any help is appreciated, Charlie
Ruby 3.1
Rails 7.0.1
You're decompressing and recompressing twice. How about (untested):
def convert_to_png(img, img_to_compress)
# load the image as a Vips Image object
vips_img = Vips::Image.new_from_buffer (URI.open(img.url) { |f| f.read }), ''
# save as a PNG string
png = StringIO.new(vips_img.write_to_buffer('.png')),
img_to_compress.attach(io: png, filename: img.filename)
end
pngsave_buffer gives you a String with binary encoding, you don't need to save a second time. Try this test program:
require "vips"
x = Vips::Image.new_from_file ARGV[0]
y = x.pngsave_buffer
puts "y.class = #{y.class}"
I see:
$ ./pngsave.rb ~/pics/k2.jpg
y.class = String
If the image is already a PNG you'll be wasting a lot of time, of course. You could add something to detect the format and skip the conversion.

Rails, Active Storage, MiniMagick - convert PDF pages to images (.png, .jpg, etc.) and upload to active storage

I am using rails, active storage and minimagick to try to convert a multi-page PDF into individual PNG images and then store the images with active storage.
I have been able to successfully load a PDF and convert the pages into png images using MiniMagick. However, I have only been able to get Minimagic to store the png images in the app/assets/images directory (as opposed to storing the png images as active storage attachments).
Does anyone know how get MiniMagick to store images with active storage?
Here is the code I'm using:
# 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
Any help would be greatly appreciated!! Thanks!
UPDATE: I got it to work and here is the final code:
# 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
NOTE: I found 3 different methods for creating tempfiles (see here) depending on the ruby version and / or preference:
# 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 is a directory for your asset sources, images from there are copied to public/assets during asset compilation (usually done on deploy), so to be able to access created images - store them somewhere in public, like public/converted/page_1.png (it will have path /converted/page_1.png)
MiniMagick is just a wrapper around ImageMagick command line utilities, so you need to save the result in a temporary location before uploading to activestorage and then use something like:
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)

Carrierwave: convert an uploaded PNG to JPG by replacing the original version (or: having versions with a different file format than original file)

I have the following model:
class ScreenshotUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
storage :file
convert :jpg
version :thumb do
process resize_to_fill: [50, 50]
end
def extension_whitelist
%w(jpg jpeg gif png)
end
version :print do
process border: ['black']
process quality: 80
end
end
The upload of the image happens via pasting an image from the clipboard via https://github.com/layerssss/paste.js and is saved as a base64 encoded string into a <textarea>, then uploaded using the https://github.com/y9v/carrierwave-base64 gem:
class Finding < ApplicationRecord
mount_base64_uploader :screenshot, ScreenshotUploader
end
In the HTML form, it looks like this:
After uploading, the result is the following files:
screenshot.png it's a PNG, not a JPG!
thumb_screenshot.jpg
print_screenshot.jpg
But I need the original file to be also converted to JPG, as I need to save disk space. How can I achieve this?
You can do it like it written on the carrier wave documentation
Just replace system("mogrify -resize '1200\>' #{file.file}") with system("mogrify -format jpg #{file.file}") and then remove original file.
Adding to Vasiliy's answer, I came up with the following:
after :store, :convert_original_to_jpg
def convert_original_to_jpg(new_file)
if version_name.nil?
system("mogrify -format jpg -quality 80 #{file.file}")
system("unlink #{file.file}") # Remove the old PNG file
model.update_column mounted_as, "#{mounted_as}.jpg" # The filename in the DB also needs to be manually set to .jpg!
end
end
While this works for creating the file, it does not when updating the file, as the new_file parameter then is nil, and thus all images are removed.
I think this is some quirk that has to do with the carrierwave-base64 gem, and I don't have any motivation to dig into this any further. So the proposed solution might not be too useful, but for the sake of documentation I wanted to post it here.
In my special case, I decided to let go of the idea of saving disk space by converting PNG to JPG. Instead, I simply set process quality: 80 to save at least some space on the versions.
For the original PNG (which is saved in lossless state by carrierwave-base64 gem), I simply use the following code to shrink its quality:
after :store, :optimise_images
def optimise_images(new_file)
return if Rails.env.test? # Optimising consumes quite some time, so let's disable it for tests
if version_name.nil?
image_optim = ImageOptim.new pngout: false,
svgo: false,
pngcrush: false,
optipng: false,
pngquant: {allow_lossy: true}, # Everything disabled except pngquant, to keep the performance at a good level
advpng: false
image_optim.optimize_images!(Dir["#{File.dirname(file.file)}/*.png"])
end
end

Can we convert a .msg to jpeg using imagemagick?

I need to convert a .msg file to an jpeg image. Can we do it using ImageMagick ?
I tried to run the below command,
magick mail.msg mail.jpeg
but it gives an error :
magick: no decode delegate for this image format `MSG' # error/constitute.c/ReadImage/512.
I need .msg to be converted into an image.
Can you please suggest if that is possible.
Thanks,
Abha

Resources