概要
Rails学習者なら一度は使用したことがあるであろう有名なGem「MiniMagick」について、詳しく調べてみると学びが多かったのでまとめてみました。
実務経験を積んでいる方なら当たり前の知識かもしれませんが、深く考えずにGemを使用していた私にとっては興味深い内容でした。
そもそも「ImageMagick」を各言語で使いやすくしたものの一つがMiniMagickということすら知りませんでした。(^^;;)
同じようなレベルの方にとっては面白い内容だと思います。
https://imagemagick.org/script/develop.php
環境
ruby 2.7.1
ImageMagick 7.0.10-23
MiniMagick 4.10.1
ImageMagickの脆弱性
ImageMagickは扱えるファイルの数が多いことなどから、脆弱性が多く報告されており、使用の際は環境に応じた対策をとる必要があるようです。
参考
さようなら ImageMagick
「さようなら ImageMagick」の考察
ImageMagickを使うWebアプリのセキュリティ
【実験】Railsのよく紹介されているようなバリデーション
例えば以下のバリデーションでは、sample.pdf
というファイルをsample.jpg
と拡張子を書き換えただけでバリデーションに掛からなくなります。
def image?(picture)
picture.content_type.in?(%("image/jpeg image/jpg image/png")) && picture.content_type != ""
end
ローカル環境で試しにsample.pdf
をsample.jpg
に書き換えて以下のコードを実行し、書き出されたファイルの拡張子をpdfに戻したところ、pdfとして表示することができました。
また、typeは拡張子のJPEG
ではなくpdfのAI
として認識されます。
require "mini_magick"
image = MiniMagick::Image.open("sample.jpg")
puts image.type
image.write("output.jpg")
=> AI
危険性についてはうまく説明できませんが、想定していたバリデーションを簡単に抜けられていることを考えると怖いですね。
参考記事では、policy.xml
を設定するように紹介していましたが、MiniMagickでの方法がよく分からなかったので、調べ次第追記します。
簡易的にこんなのもありかな??
types = [ "JPEG", "PNG" ]
types.include?(image.type)
最適化と実行速度
resizeとstripオプションくらいしか使用したことがなかったのですが、ImageMagickには他にも多くのオプションが用意されており、MiniMagickでも全てのオプションを使用することができます。
GoogleDevelopersで紹介されていたJPEG画像の最適化コードを元に簡単な実験をしてみたのですが、実行方法次第で4倍ほど実行速度に差がありました。
参考
画像を最適化する
ImageMagick の JPEG オプション
サンプルコード
使用画像:
サイズ: 10.8M
大きさ: 6000×6000
require "bundler/setup"
require "mini_magick"
require 'benchmark'
result = Benchmark.realtime do
MiniMagick::Tool::Magick.new do |magick|
magick << "images/test.jpg"
magick.sampling_factor "4:2:0"
magick.strip
magick.interlace "JPEG"
magick.colorspace "sRGB"
magick.quality "85"
magick << "output.jpg"
end
#処理概要 2.2035720000058063s
#処理概要 2.2015189999947324s
#処理概要 2.204152000005706s
#サイズ 10.8M => 4.9M
convert = MiniMagick::Tool::Convert.new
convert << "images/test.jpg"
convert.sampling_factor "4:2:0"
convert.strip
convert.interlace "JPEG"
convert.colorspace "sRGB"
convert.quality "85"
convert << "output.jpg"
convert.call
#処理概要 2.202056000001903s
#処理概要 2.1908320000002277s
#処理概要 2.207652999997663s
#サイズ 10.8M => 4.9M
image = MiniMagick::Image.open("images/test.jpg")
image.sampling_factor "4:2:0"
image.strip
image.interlace "JPEG"
image.colorspace "sRGB"
image.quality "85"
image.write("output.jpg")
#処理概要 9.05225000000064s
#処理概要 9.08197300000029s
#処理概要 8.971012000001792s
#サイズ 10.8M => 5.4M
cmd = "convert images/test.jpg -sampling-factor 4:2:0 -strip -quality 85 -interlace JPEG -colorspace sRGB output.jpg"
system cmd
#処理概要 2.168391999999585s
#処理概要 2.064037999996799s
#処理概要 2.167836000000534s
#サイズ 10.8M => 4.9M
end
puts "処理概要 #{result}s"
あとがき
「実行速度遅いんだよな〜」「思ったより圧縮されないんだよな〜」と思いつつも、表面撫でたような調べ方しかしていなかったので、ImageMagickがこんなにも高機能なソフトウェアだと知りませんでした。
MiniMagickの書き方も色々あったんですね。
MiniMagick::Image.open
、MiniMagick::Image.new
くらいしか使っていなかったので驚きでした。
そして、全てREADMEに書いてあったのも...
日本語記事に甘えず、まずはREADMEを隅々まで読もうと、もう何度目になるか分からない決意を新たにしました。。。