LoginSignup
162
146

More than 3 years have passed since last update.

Active StorageのVariantの指定方法いろいろ

Last updated at Posted at 2019-01-11

静岡市でWeb開発しているフリーランスのkazuomatzです。

Rails 5.2で利用可能になったファイルアップロードに利用できるActive Storageを使ってみました。

これまでは画像のアップロードにはPaperclipを使っていました。Paperclipにはアップロードした時に、自動的に画像サイズを変更して、何パターンかの画像を保存する機能がありました。正方形にクリップしたサムネイル画像もアップロード時に作成できるので、無駄に大きな画像をサムネイル表示するようなページを回避できるので重宝していました。

Paperclipでの実装

app/models/user.rb
class User < ApplicationRecord
    has_attached_file :avatar,
    styles: { m: '1024x1024>', s: '800x800>',thumb: '640x640#' }
end
app/views/users/show.html.erb
<div>
    <span>長辺を1024pxにしてリサイズされた画像を表示</span>
    <%= image_tag @user.avatar(:m)%>
</div>

<div>
    <span>長辺を800pxにしてリサイズされた画像を表示</span>
   <%= image_tag @user.avatar(:s)%>
</div>

<div>
    <span>サムネイル画像(W:640 x H:640にクロップされた画像)を表示</span>
    <span><%= image_tag @user.avatar(:thumb)%></span>
</div>

このように、あらかじめ必要とされる画像サイズを決めておけば、用途用途で使い分けることができるのでとても便利です。

しかし、Webサイトを長いこと運用していると、のちのち、横幅1920pxの画像を表示したい、というようなケースが出てきて困ったことになりました。

Modelに新しいサイズを再定義したとしても、これまで登録された画像はそのサイズで保存されていないので、再生成するようなプログラムを書いてまわすみないなことをやればいいですが、それも面倒です。

そのような時にActive Storageの Variantを使うととっても便利です。Variantを使うと新しい画像サイズが欲しくなった時に、オンデマンドで画像が生成されます。

Active Storageの準備

先ほどのPaperclipの実装をActive Storageを使って書き換えてみます。Variantを利用する場合には、ImageMagickがシステムにインストールされていることと、MiniMagick gemのロードが必要です。

Gemfile
# Use ActiveStorage variant
gem 'mini_magick', '~> 4.8'
実行
# Active Storageのインストール
$ rake active_storage:install

# DBマイグレーション
$ rake db:migrate

これで、development環境ではローカルディスクをアップロード先としてActive Storageが利用可能になります。
アップロード先をAWS S3やAzure Storage Service、Google Cloud Storage Serviceなどにし指定することもできますが、詳しくは、本家のリファレンスを参照してください。

では、先ほどのPaperclipの実装をActive Storageを使った実装に変更してみます。

Active Storageでの実装

app/models/user.rb

class User < ApplicationRecord
    has_one_attached :avatar
end

まず、Model側の定義はこれだけです。
has_one_attachedはモデルに1つのファイルを紐付けます。has_many_attachedを用いるとモデルに複数のファイルを紐付けることが可能になります。

便利なのはここからです。Viewを見てみましょう。

app/views/users/show.html.erb
<div>
    <span>長辺を1024pxにしてリサイズされた画像を表示</span>
    <%= image_tag @user.avatar.variant(resize:'1024x1024').processed %>
</div>

<div>
    <span>長辺を800pxにしてリサイズされた画像を表示</span>
    <%= image_tag @user.avatar.variant(resize:'800x800').processed %>
</div>
<div>
    <span>サムネイル画像(W:640 x H:640にクロップされた画像)を表示</span>
    <span><%= image_tag @user.avatar.variant(combine_options:{gravity: :center, resize:"640x640^",crop:"640x640+0+0"}).processed%></span>
</div>

Paperclipがあらかじめサイズを決めておいて、その中からどのサイズを使うのかを指定するという方式なのに対して、Variantでは、View側で使用したいサイズをその都度指定できるのが大きな違いです。

Active StorageではVariantが呼び出されると、保存された元画像データを指定されたサイズに変換しそのURLを返します。

processedをつけることで、すでにそのサイズで保存されて画像があれば、変換処理は行われず、即時にURLが返されますので、最初の呼び出しだけ多少時間がかかりますが、それ以降の呼び出しでは時間がかかることはありません。

Paperclipの場合、thumb:'640x640#' と指定すると画像の中心をクリップした640pxの正方形の画像を生成してくれましたが、Variantを使う場合は、以下のように書きます。

正方形にくりぬく
@user.avatar.variant(combine_options:{gravity: :center, resize:"640x640^", crop:"640x640+0+0"}).processed

Variantの指定は、ImageMagickのコマンドが利用可能です。
まず、gravity:'center'を指定し、resize:'640x640^'を指定し、画像の縦横の短辺の長さを640pxになるようリサイズします。そして、crop:'640x640+0+0'で、画像の中心点からW:640xH:640のサイズで画像を切り抜きます。
リサイズ+切り抜きといった操作をまとめて行う場合は、combine_optionsを指定する必要があります(参考:What Options Can Be Passed to the Active Storage variant Method?)。

以下の例だと、正しい位置からクロップできませんでした(gravityが効いていない)。
Rails 6になってから、combine_optionsを使用しなくても、下記のコードで動作するようになっているようです。

正方形にくりぬく
@user.avatar.variant(gravity: :center, resize:"640x640^", crop:"640x640+0+0").processed

また、Rails 6.1からはcombine_optionsは利用できなくなるとのことです。

参考記事1
参考記事2

もし、後から横幅1920pxサイズの画像が欲しくなった場合も簡単に対応できます。

app/views/users/show.html.erb
<div>
    <span>長辺を1920pxにしてリサイズされた画像を表示</span>
    <%= image_tag @user.avatar.variant(resize:'1920x1920').processed %>
</div>

Webサイトの運営上、ページのリデザインを行う場合などに、簡単に適正サイズの画像が生成できるのは大きなメリットだと思います。大きな画像をレンダリングの際にスタイルシートでwidth/heightを指定して縮小(拡大)表示することはよくあることですが、レンダリングスピードや転送速度を考慮するとあまりよいことでありません。

その辺りを考慮すると、Active Storageを使うメリットは大きいと思います。

おまけ

VariantはImageMagickのコマンドを受け付けますので、以下のようなことも簡単にできてしまいます。ご利用は計画的に!

GrayScale表示
@user.avatar.variant(resize:'200x200',type: :grayscale).processed

反転
@user.avatar.variant(resize:'200x200',flop:true).processed

パラメータが必要のないオプションはtrueを指定します。

ぼかし
@user.avatar.variant(resize:'200x200',blur:50).processed

回転
@user.avatar.variant(resize:'200x200',rotate:30).processed

ボーダーをつける
@user.avatar.variant(combine_options:{resize:'200x200',border:'5',bordercolor:'red'}).processed

162
146
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
162
146