Open4

【Rails 7】Amazon S3に画像登録する前にリサイズを行う(Gem libvips)

いさみんいさみん

タイトルが入りきらなかったのでサブタイトルをここに書く(長いっ!)

ActiveStorageにてgem ActiveStorage Validationのvariantメソッドを使い
image_processing経由でlibvipsにて画像サイズの変換処理をする

使うやつは全部書いておかないとね。

環境

Mac OS
Docker
Ruby 3.3.6
Rails 7.2.2.1

Amazon S3に画像保存する前にリサイズして保存容量を抑えよう!!

登録から1年間AWS S3の無料利用枠があるとはいえ、使えるのは5GBまで。
そこからWebサービスで写真の枚数の登録がさらに増えていくとなると1MBでさえ抑えておきたい。

そこでRailsガイドのActiveStorage の概要より、ActiveStorageで、アプリケーションにアップロードした画像の変形を行っていきたいと思います。

1.1 要件をそのまま書くと

Active Storageの多くの機能は
Railsによってインストールされないサードパーティソフトウェアに依存しているため
別途インストールが必要です。

libvips v8.6以降(またはImageMagick): 画像解析や画像変形用
ffmpeg v3.4以降: 動画プレビュー、ffprobeによる動画/音声解析
popplerまたはmuPDF: PDFプレビュー用
画像分析や画像加工のためにimage_processing gemも必要です。
Gemfileのimage_processing gemをコメント解除するか、必要に応じて追加します。

gem "image_processing", ">= 1.2"
ImageMagickは、libvipsに比べて知名度が高く普及も進んでいます。
しかしlibvipsは10倍高速かつメモリ消費も1/10です。
JPEGファイルの場合、libjpeg-devをlibjpeg-turbo-devに置き換えると2〜7倍高速になります。

⚠️注意⚠️
サードパーティのソフトウェアをインストールして使う前に、
そのソフトウェアのライセンスを読んで理解しておきましょう。
特にMuPDFはAGPLでライセンスされており、利用目的によっては商用ライセンスが必要です。

主要となっているのはImagemagickらしいですが、
今回はあまり記事のないlibvipsを使ってやっていこうかなと思います。

いさみんいさみん

さて。まず………なにすりゃいいんだ?w

毎回ながら迷走する自分…
・gem libvipsで調べたら出てきたけど、名前が「ruby-vips」……???
・libvipsとどう違うんだ?
・そもそもgem image_processingって何してるん?
etc...

ひとまず…

関係性としては

[Railsサーバ]---[ActiveStorage]---[image_processing]---[libvips]

image_processing:Railsで使うコードを翻訳(パイプライン)
libvips:画像処理(C言語)

どう投げるか?

  1. ユーザが画像アップロード
  2. ActiveStorageにて「variant(resize_to_limit: [300, 300])」というvariant設定で画像加工を指示
  3. image_processingにてlibvipsで使うC言語の命令「vips.resize(300, 300)」に翻訳
  4. libvipsが画像処理を行う(ImageMagick <mini_magick>も同様)

その他C言語だと。。。

C言語 = メモリ効率◎、処理速度◎

と言ったところかな…?

いさみんいさみん

記載いただいている内容をやっていく。

実装
画像保存部分の設定

記載通りジェムをインストール。

ちなみにimage_processingをインストールするとmini_magickもインストールされています(gemfile lockを確認)


→ほんとだ!ruby-vipsも入ってる!(疑問に思ってたgemが一緒に入ってる)

必要なgemのインストールが終わったら、次はActiveStorageの有効化を行っていきます

ActiveStorageは有効済みなので、私は今回飛ばします。。。

画像用のバリデーションを追加

(コードコピペしてます)

app/model/post.rb
class Post < ApplicationRecord
  # 省略
  has_one_attached :image   # さっき追加したやつ
  
  # ファイルの種類とサイズのバリデーション(gem ActiveStorage Validationを使用)
  ACCEPTED_CONTENT_TYPES = %w[image/jpeg image/png image/gif image/webp].freeze
  validates :image, content_type: ACCEPTED_CONTENT_TYPES,
                    size: { less_than_or_equal_to: 5.megabytes }

  # 省略
end
  • .freeze :
     Rubyでオブジェクトを「凍結」するメソッドです。
     簡単に言うと、そのオブジェクトを変更不可(イミュータブル)にするためのものです。

…なるほど。

jpeg、gif、webpに固定して、ACCEPTED_CONTENT_TYPESという引数に入れ、
画像のvalidate(登録条件)をその引数に入っている拡張子で、5Mバイトいかにしろってことか。
(あ。書いてた。<おいっ)

実際、Gemを使わなくてもいいらしいけど、このGemを使うことで
コードがスッキリするみたい。

Modelだけで画像形式を制限する方法(Gemなし)
class Spot < ApplicationRecord
  has_one_attached :spot_image

  validate :correct_image_type

  private

  def correct_image_type
    return unless spot_image.attached?

    acceptable_types = ["image/jpeg", "image/png", "image/jpg"]

    unless acceptable_types.include?(spot_image.content_type)
      errors.add(:spot_image, "はJPEGまたはPNG形式のみアップロード可能です")
    end
  end
end

Gemありの場合は以下の通り1行にまとめられます。ただこれは非推奨。

Gemありの場合(非推奨)
validates :spot_image, content_type: ['image/png', 'image/jpg', 'image/jpeg']

推奨は画像のタイプをビューファイルで使用できるようにするために

Gemありの場合(推奨)
class User < ApplicationRecord
  ACCEPTED_CONTENT_TYPES = ["image/jpeg", "image/png", "image/webp"].freeze
  has_one_attached :avatar

  validates :avatar, content_type: ACCEPTED_CONTENT_TYPES
end

他のファイルでファイル形式を決めるため引数に入れることで
ビューファイルにてその引数が使えるようになるみたい。

ビューファイルにて
<%= form_with model: @user do |f| %>
  <%= f.file_field :avatar,
                   accept: ACCEPTED_CONTENT_TYPES.join(',') %>
<% end %>

・・・そもそもwebpってなにっ?

端末 拡張子 MIMEタイプ 備考
iPhone(カメラ) .heic image/heic または image/heif ⚠️ iOS 11以降デフォルト
Android(カメラ) .jpg / .jpeg image/jpeg ✅ 標準的
一部のAndroid .webp image/webp ❗ 使用機種による
スクショ(iOS/Android) .png image/png ✅ 安定
デジカメ .jpg / .jpeg image/jpeg ✅ 安定

→一部アンドロイドで使われる拡張子っぽい?
 iPhoneの画像は基本的にクロームなどで使うとjpegに変えてくれるんだとか。
 ・・・スマホからPCに画像を飛ばしたらHEICのままだった気がするので、image/heic入れておこうかしら。

ひとまず疑問はここまでかな。

:imageをストロングパラメーターに追加

から

ここまで設定できればOK!

は飛ばします。が。

Amazon S3に画像保存するためには以下が必要。
今回はスキップします。やり方は軽く書きますが…これらは各自調べてみてください。

1. Gemのインストール(gem 'aws-sdk-s3')
2. config/storage.yml にS3設定を追加
3. config/environments/development.rb 等で保存先を指定(本番環境はproduction)
4. .env や credentials.yml.enc にAWSの認証情報を記載
5. バケット作成(AWS側)
6. アップロードがS3に反映されるか確認

その他参考にしたサイトも貼っておきます。
Railsガイド v7.2_Active Storage の概要

[Rails]Active Storageの画像をAWS S3へ保存する

S3へのアクセスキーとシークレットキーをサクッと作成してみよう

Railsのキー設定でつまづいた話(secret_key_base)