Ruby on Railsに用意されている便利機能の一つにActive Storageがあります。Active Storageを使うと画像やPDFなどのサイズの大きなファイルの取り扱いをすることができます。
単体のファイルのみでなく、複数枚のファイルをDBのデータと紐づけたり、画像の加工やダウンロードすることもできます。
ここでは、ActiveStorageの使い方を実例を踏まえて解説しています。
ActiveStorageとは?
ActiveStorageとは、Rails5.2以降から追加されたもので、Rails標準のgemで画像などのファイルをアップロードが簡単にできる機能です。
既存のテーブルのデータ(レコード)と画像を紐づけて管理することができます。
ActiveStorageのインストール
ActiveStorageを使うためには、DB上に必要なテーブルを作成する必要があります。
rails active_storage:install
でインストール後にマイグレーションを行います。
rails active_storage:install
rails db:migrate
マイグレーションを行うと次の3つのテーブルが生成されます。
テーブル名 | 内容 |
---|---|
active_storage_blobs | アップロードしたファイルを保存するテーブル(Blob型) |
active_storage_attachments | アップロードしたファイルデータとActive Recordを紐付けるための中間テーブル |
active_storage_variant_records | アップロードしたファイルのデータのVariantに関する情報を保存するテーブル |
実例
rails active_storage:installを実行します。
# rails active_storage:install
Copied migration 20210811020333_create_active_storage_tables.active_storage.rb from active_storage
すると、次のようなマイグレーションファイルが生成されます。
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.string :service_name, null: false
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
create_table :active_storage_variant_records do |t|
t.belongs_to :blob, null: false, index: false
t.string :variation_digest, null: false
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
次の3つのテーブルを生成する処理が記述されています。
- active_storage_blobs
- active_storage_attachments
- active_storage_variant_records
マイグレーションを実行します。
# rails db:migrate
== 20210811020333 CreateActiveStorageTables: migrating ========================
-- create_table(:active_storage_blobs, {})
-> 0.1982s
-- create_table(:active_storage_attachments, {})
-> 0.0799s
-- create_table(:active_storage_variant_records, {})
-> 0.0805s
== 20210811020333 CreateActiveStorageTables: migrated (0.3589s) ===============
DB上にテーブルが生成されました。また、Rails上ではマイグレーションの結果がdb/schema.rbに反映されます。
以上でActiveStorageの導入は完了です。
テーブルとActiveStorageを紐づける
usersテーブルの個々のデータに画像を添付できるようにします。
画像を紐づけるテーブルを作成
まずは、scaffoldでUserモデルやコントローラなど一連のファイルを生成します。マイグレーションファイルも生成されるので、rails db:migrate
でDBに反映します。
rails g scaffold user name:string, age:integer
rails db:migrate
モデルファイルの編集
ActiveStorageを使って画像などのデータを既存のテーブルと紐づけるために、モデルファイルにhas_one_attached :カラム名
を追加します。
シンボルで指定するカラム名でそのデータを扱うので、わかりやすい名前にします。(画像なら :image、動画なら :movie、アバターなら :avatar など)
class User < ApplicationRecord
has_one_attached :image
end
has_one_attachedはActiveStorageで使う指定方法で、指定したシンボルを自分のカラムのように扱うことができます。
コントローラの編集
関連付けたActiveStorageのデータをパラメータとして受け取れるよう、ストロングパラメータに対象のカラム名を追加します。
private
(省略)
def user_params
params.require(:user).permit(:name, :age, :image)
end
end
これで、imgaeカラムのデータを受けとれるようになります。
フォームの編集
ビューファイルを変更して、画像を添付する(:image)に対応する入力欄や表示欄を作成します。
フォームの中に、form.file_field
以下を追加します。
<%= form.file_field :カラム名 %>
scaffoldを使った場合フォーム部分は _form.html.erbというバーシャルに切り出されています。
<%= form_with(model: user) do |form| %>
<% if user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% user.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :age %>
<%= form.number_field :age %>
</div>
<%# 追記 %>
<div class="field">
<%= form.label :image %>
<%= form.file_field :image %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
これで、編集画面や新規作成画面にアクセスしたときに画像を追加するための項目が表示されます。
新規作成画面(/users/new)
編集画面(/users/:id/edit)
ビューファイルの変更
アップロードした画像をブラウザに表示するために、詳細ページと一覧ページを編集します。
画像を表示するには、image_tagを使います。
<%= image_tag モデルのインスタンス.画像カラム名 %>
詳細ページの編集
画像を添付しない場合もあるので、attached?メソッドを使って、画像が存在するときのみ表示するようにします。
以下のコードを追加します。
<% if @user.image.attached? %>
<p>
<strong>Image:</strong>
<%= image_tag @user.image %>
</p>
<% end %>
▼全体像
<p id="notice"><%= notice %></p>
<p>
<strong>Name:</strong>
<%= @user.name %>
</p>
<p>
<strong>Age:</strong>
<%= @user.age %>
</p>
<% if @user.image.attached? %>
<div>
<p><strong>Image:</strong></p>
<%= image_tag image, size: '400x300' %>
</div>
<% end %>
<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>
一覧ページの編集
一覧ページには画像の枚数を表示するようにします。画像がないときは0を表示するようにします。
<td><%= user.image.attached? ? 1 : 0 %></td>
▼全体像
<p id="notice"><%= notice %></p>
<h1>Users</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Images</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @users.each do |user| %>
<tr>
<td><%= user.name %></td>
<td><%= user.age %></td>
<td><%= user.image.attached? ? 1 : 0 %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New User', new_user_path %>
以上でブラウザに画像を表示する準備は完了です。
ブラウザで確認する
ブラウザ上で画像を保存し、表示できるかを確認します。
新規ユーザー作成ページ(/users/new)に移動し、「ファイルを選択」で画像を選びます。
↓ ユーザーの新規作成ボタン(Create User)をクリック
添付した画像が表示されればOKです。
↓ 一覧画面に遷移(/users)
一覧画面に遷移して、画像の枚数が表示されているかを確認します。
Imagesの列に画像がある場合は「1」画像がない場合は「0」と表示されています。
以上で、ActiveStorageを使った画像添付は完了です。
(参考)ActiveStorageの画像のコンパイル結果
image_tagメソッドを使って、ActiveStorageと関連付けたモデルインスタンスをsrcとして指定したときのコンパイル結果は、/rails/active_storage/blobs/redirect/配下のファイルを指すようになります。
<%= image_tag @user.image %>
↓ コンパイル
<img src="http://localhost:3001/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBDdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--59e9ed677277e36ba37be3f09da4a251a8db8d66/hills.jpg">
複数枚の画像を添付する
上記のhas_one_attached
を使った方法では1つのレコードに対し画像を1枚しか添付できません。
ここでは、1つのレコードに対し複数枚の画像を添付できるようにします。
変更内容
変更する内容は以下になります。
- モデルをhas_many_attachedにし、値を複数形のimagesにする。
- ストロングパラメータを配列 images: [] にする。
- フォームにmultiple: trueを追加し複数選択を許可する。
- 詳細表示ビューのeachメソッドを使って、複数枚表示できるようにする。
- 一覧表示ビューでuser.images.countを使って枚数をカウントする。
モデルファイルの変更
モデルファイルにhas_many_attached
を追加して、複数のActiveStroageのblobファイルを紐づけられることを明示します。
また、blobの名称も複数形にします。(例 :images)
class User < ApplicationRecord
has_many_attached :images
end
ストロングパラメータの変更
imageプロパティの値が配列になるため、ストロングパラメータを修正します。
private
def user_params
params.require(:user).permit(:name, :age, images: [])
end
フォームの変更
デフォルトではフォームは複数選択ができない状態です。multiple: trueを追加することで、複数選択を許可します。
<div class="field">
<%= form.label :images %>
<%= form.file_field :images, multiple: true %>
</div>
詳細ページの変更
画像が配列として渡されるようになるため、eachメソッドを使ってデータを一つ一つ抜き出して処理するようにします。
<% if @user.images.attached? %>
<div style="width: 700px;">
<p><strong>Image:</strong></p>
<% @user.images.each do |image| %>
<%= image_tag image, width: 300, height: 200 %>
<% end %>
</div>
<% end %>
一覧ページの変更
添付してある画像の枚数を表示するようにします。
画像の枚数はオブジェクトに対してcountメソッドを使えば取得できます。
<%= user.images.attached? ? user.images.count : 0 %>
ブラウザで確認
ブラウザで複数の画像を添付します。
↓ 画像を選択
↓ 作成ボタンをクリック
1人のユーザーに対して複数の画像を追加することができました。
↓ 一覧画面に遷移
Imagesの列に各ユーザーに添付した画像の枚数が表示されています。
画像表示と個別削除機能の実装
上記の状態では、編集ページで情報を更新するときに、画像をゼロから選び直さなければいけません。
ここでは、編集画面で選択済みの画像を表示して、個別に削除できる機能を追加します。
変更内容
- フォームで削除したい画像を選択可能にする
- updateアクションに画像を削除する処理を追加する
- 上書きしないようapplication.rbを設定する
フォームで削除したい画像を選択可能にする
まずは、フォーム(_form.html.erb)で削除したい画像を選択できるようにします。
画像の横にチェックボックスを設置し、チェックした画像のidをパラメータとして渡すようにします。
チェックボックスは小さいので画像と連動するようにします。
<% if user.images.attached? %>
<p>選択済みの画像(削除する画像にチェックをつけてください)</p>
<div style="display: flex; flex-wrap: wrap; width: 750px;">
<% @user.images.each do |image| %>
<div style="display: flex; margin:5px 5px 5px 0;">
<%= form.check_box :image_ids, { multiple: true }, image.id, false %>
<label for="user_image_ids_<%= image.id %>" >
<%= image_tag image, size: '300x200' %>
</label>
</div>
<% end %>
</div>
<% end %>
form.check_box
form要素に対するcheck_boxメソッドを使うと、チェックボックスを生成することができます。
f.check_box(メソッド名 [, オプション, checked_value = "1", unchecked_value = "0"])
check_boxメソッドがとれる引数の数は4つです。(後ろの3つは省略可能)
引数の番号 | 項目 | 内容 |
---|---|---|
1 | メソッド名 | パラメータのプロパティ名。この名前で渡されたデータにアクセスします。 |
2 | オプション | { オプション名:値,,, } の形で複数指定できます。 |
3 | checked_value = “1” | チェックされたときに送信するvalueの値です。trueにすれば1が入ります。自分で数値を指定できます。(true or 数値) |
4 | unchecked_value = “0” | チェックされていないときに送信するvalueの値です。falseにすれば何も送りません。trueにすると0になります。 |
ここでは、以下のように設定しています。
<%= form.check_box :image_ids, { multiple: true }, image.id, false %>
引数の番号 | 項目 | 指定 | 内容 |
---|---|---|---|
1 | メソッド名 | :image_ids | 渡されたデータにparams[:user][:image_ids]でアクセスできます。( :userは、modelで指定したシンボルが入りますform_with(model: user) ) |
2 | オプション | { multiple: true } | 複数データの送信が可能になります。nameの値を配列として格納できます。name[image_ids][] |
3 | checked_value = “1” | image.id | valueにeachメソッドで抜き出したblobオブジェクトのid番号が入ります。 |
4 | unchecked_value = “0” | false | 選択されていない場合は何もしません。 |
image_tag
image_tagで指定した画像を表示させます。
<%= image_tag image, size: '300x200' %>
ActiveStorageの場合は、第1引数に画像オブジェクトを指定すれば、対象の画像パスが入ります。
sizeオプションで表示する画像の 横幅 x 高さ を指定しています。
label
画像を選択したときにチェックボックスが反応するように、image_tagをlabelタグで囲みます。
for属性の値をinputタグのid名と一致させることで、要素が連動します。
<label for="user_image_ids_<%= image.id %>" >
form.check_boxで生成されるinputタグのid名は、id="user_image_ids_画像のid番号"
です。
for属性にも同じ値をしていします。タグの中で変数を使う場合は <%= 変数 %>
とします。user_image_ids_<%= image.id %>
style属性
見栄えを調整するため、styleタグを適用しています。(ここでは簡略化のためstyleタグを使っています。class名やid名をつけてcss/scssファイルでスタイルを適用するのが推奨です)
ブラウザで確認
フォーム部分の見た目は次のようになります。画像をクリックしてもチェックボックスが反応します。
この状態では次の問題点があります。
- 画像の個別削除機能は実装されていない。
- 新しい画像を添付すると、既存の画像がすべてなくなる(上書きされる)
updateアクションに画像を削除する処理を追加する
画像を個別に削除する機能を実装します。
def update
#添付画像を個別に削除
if params[:user][:image_ids]
params[:user][:image_ids].each do |image_id|
image = @user.images.find(image_id)
image.purge
end
end
respond_to do |format|
if @user.update(user_params)
format.html { redirect_to @user, notice: "User was successfully updated." }
format.json { render :show, status: :ok, location: @user }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @user.errors, status: :unprocessable_entity }
end
end
end
パラメータで渡されるデータ
なお、画像をチェックした場合に渡されるparamsのimage_idsは以下のようになります。
#<ActionController::Parameters {"name"=>"test1", "age"=>"24", "image_ids"=>["85", "86", "91", "92"]} permitted: false>
削除対象の画像があるか確認
form.check_boxで生成したinputタグにはname="user[image_ids][]" value="画像のid番号"
が付与されます。
画像をチェックすると、params[:user][:image_ids]
の中にvalueの値が格納されて渡されます。何も選択されていない場合は、[:image_ids]
が渡されません。
そもそも削除対象の画像があるかを確認するためにif文で存在確認を行います。
if params[:user][:image_ids]
ActiveStorageの画像を削除する方法
ActiveStorageで紐づけたblobファイルを削除するには、purgeメソッドを使います。
モデルインスタンス名.blob名.purge
has_many_attachedで複数選択を可能にしている場合は、削除したいデータを指定します。
例えば、id番号で指定する場合は次のようにします。
モデルインスタンス名.blob名.find(id番号).purge
以上で画像の個別削除機能の実装は完了です。
この状態で画像を選択し更新をクリックすると、対象の画像を削除することができます。
↓ 更新ボタン(Update User)をクリック
ただし、新しい画像を添付すると、既存の画像がすべてなくなる(上書きされる)という問題が残っています。
上書きしないようapplication.rbを設定する
ActiveStorageの上書きはRails6からデフォルトでONになった機能です。Rails5のようにデフォルトで上書きを許可しないようにするには、application.rbに1行追加するのみです。
module RailsVue
class Application < Rails::Application
(省略)
#ActiveStorage上書きしない
config.active_storage.replace_on_assign_to_many = false
end
end
(参考)
以上で機能の実装は完了です。
application.rbの変更を反映させるために、Railsのアプリケーションを再起動します。
ブラウザで確認
上書きされずに画像を更新できるか確認します。
以下のように、既に画像が3枚選択されている状態で新しい画像を選択します。
↓ 更新ボタン(Update User)をクリック
上書きすることなく画像を添付することができました。
続いて、画像の削除と追加を同時に行ってみます。新たに画像を選択し、左上の1枚以外を削除対象とします。
↓ 更新ボタン(Update User)をクリック
期待どおりに動作させることができました。
ファイルにリンクを設置する方法
url_forを使えば、ActiveStorageで紐づけたファイルへのリンクを設置することができます。
url_for(モデルインスタンス名.blobファイルの名称)
実例
画像をクリックしたときにブラウザ上にその画像を直接表示するようにします。
詳細ページ(show.html.erb)の画像をaタグで囲みます。
<% if @user.images.attached? %>
<div style="width: 700px;">
<p><strong>Image:</strong></p>
<% @user.images.each do |image| %>
<a href="<%= url_for(image) %>">
<%= image_tag image, size: '300x200' %>
</a>
<% end %>
</div>
<% end %>
ブラウザ上で画像をクリックすると、画像のページに飛ぶことができます。
↓ 画像をクリック
URLはactive_storageのディレクトリを指しています。
http://localhost:3001/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDVG9JYTJWNVNTSWhZM0JqWkRrNU1ESjNkbm80TkdVeWFIbHVhMnhrTW1WNk9IWTFPUVk2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpT1dsdWJHbHVaVHNnWm1sc1pXNWhiV1U5SW5ObFlTNXFjR2NpT3lCbWFXeGxibUZ0WlNvOVZWUkdMVGduSjNObFlTNXFjR2NHT3daVU9oRmpiMjUwWlc1MFgzUjVjR1ZKSWc5cGJXRm5aUzlxY0dWbkJqc0dWRG9SYzJWeWRtbGpaVjl1WVcxbE9ncHNiMk5oYkE9PSIsImV4cCI6IjIwMjEtMDgtMTVUMDE6MzA6MjEuNjkyWiIsInB1ciI6ImJsb2Jfa2V5In19–bf0b8b3eb2821b6d8c77ddeb2e81547d71338860/sea.jpg
link_toとimage_tagの組み合わせ
上記はaタグでimage_tagを囲みましたが、link_toの中でimage_tagを使うこともできます。
<% if @user.images.attached? %>
<div style="width: 700px;">
<p><strong>Image:</strong></p>
<% @user.images.each do |image| %>
<%= link_to image_tag(image, size: '300x200'), url_for(image) %>
<% end %>
</div>
<% end %>
link_to 'アンカーテキスト', 'リンク先のパス'
で、アンカーテキストにimage_tag
を、URLにurl_for(image)
を指定します。
ファイルのダウンロード機能の実装方法
ActiveStorageでは、blobファイルに対してdownloadメソッドを使うことで、バイナリデータをメモリに保存することができます。
保存したデータに対して、send_dataを使えば、ダウンロード処理が実行できます。
ダウンロード機能の実装手順
- ダウンロード用のルーティング作成
- ダウンロードアクションの作成
- ダウンロードリンクの設置
ダウンロード用のルーティング作成
まずはダウンロード用のルーティングを作成します。users/:id/downloadにアクセスしたときに、usersコントローラのdownloadアクションを実行するようにします。
scaffoldを使って作成しているため、routes.rbにはresources :user
が自動で追加されています。
:idを使った独自のルーティングを追加したいのでmember
を使います。
resources :users do
get :download, on: :member
end
onオプションを使って、シンボル :member を指定することもできます。
resources :users do
member do
get :download
end
end
rails routesを実行して、以下のようなルーティングが追加されていれば正しく設定できています。
root@e1c25463cf4f:/rails-test# rails routes
Prefix Verb URI Pattern Controller#Action Controller#Action
download_users GET /users/:id/download(.:format) users#download
ダウンロードアクションの作成
続いて、usersコントローラーにdonwloadアクションを追加します。
@user = User.find(params[:id])
を実行したいので、before_actionのset_userにdownloadアクションを追加します。
before_action :set_user, only: %i[ show edit update destroy download]
(省略)
def download
image = @user.images.find(params[:image_id])
file = image.download
send_data(file, filename: "picture#{params[:image_id]}.jpg")
end
downloadメソッド
downloadメソッドはblobファイルのバイナリをダウンロードしてくるメソッドです。
ここでは選択した画像のid番号をパラメータで渡して、findメソッドを使ってblobファイルを指定し、変数imageに代入しています。
そして、downloadメソッドを使って、バイナリデータを変数fileに保存します。
send_data
send_dataメソッドを使うとバイナリデータを出力できます(ダウンロードできます)。
send_data(送るデータ [, オプション])
:filenameオプションを使って、ファイル名を指定しています。ファイル名がユニークになるように、ファイル名に画像のid番号を指定しています。
send_data(file, filename: "picture#{params[:image_id]}.jpg")
ダウンロードリンクの設置
最後に、クリックした画像をダウンロードできるように、詳細ページの各画像にダウンロードリンクを設置します。
<% if @user.images.attached? %>
<div style="width: 700px;">
<p><strong>Image:</strong></p>
<% @user.images.each do |image| %>
<%= link_to image_tag(image, size: '300x200'), download_user_path(@user, image_id: image.id) %>
<% end %>
</div>
<% end %>
ダウンロード用のルーティング名
downloadアクションを実行するためのルーティング名は、先ほど設定したdownload_usersです。
_pathメソッドを使って相対パスを生成します。
_pathで任意のパラメータを渡す
今回は画像のid番号を指定するために、_pathでid番号をパラメータとして渡します。
_pathで追加のパラメータを渡すには、引数でキー名と値を指定します。
ここではシンボルを使って、image_id: image.id
としています。
これは、:image_idというシンボルをキー名として値にimage.idを渡しすための省略記法です。
以上でActiveStorageで連携した画像ファイルのダウンロードの準備が整いました。
ブラウザで確認
詳細ページを開き、ダウンロードしたい画像をクリックします。
↓ 画像をクリック
URLがlink_toで指定したパラメータがついた状態になり、画像のダウンロードが開始します。
/users/1/download?image_id=98
ダウンロードしたファイル名にパラメータで渡したIDが入っています。
↓ ダウンロードした画像をダブルクリック
画像をダブルクリックすると、ダウンロードしたJPEGが開きます。
以上でActive Storageによる画像のダウンロード機能の実装は完了です。
画像を変換する
image_tag image, size: '300x200'
のようにimgae_tagメソッドでsizeオプションを使うと、画像のサ画像を表示する枠のサイズを指定することができます。
この場合、表示したい枠に対して、不必要に大きな画像が用いられることがあり、速度低下などの問題があります。
Active Storageでは、variantメソッドを使ってBlobファイルを呼び出すときに、画像自体のサイズを変更することもできます。
variantの有効化
variantを有効にするには、image_processingのgemをインストールします。
Gemfileに以下を追記します。(※Railsのバージョンによってはデフォルトでコメントアウトになっているので、有効化します。)
# Use Active Storage variant
gem 'image_processing', '~> 1.2'
gemをインストールするため、bundle install
を実行します。
# bundle install
Fetching gem metadata from https://rubygems.org/............
(省略)
Fetching ruby-vips 2.1.2
Installing ruby-vips 2.1.2
Fetching image_processing 1.12.1
Installing image_processing 1.12.1
image_processingとvipsがインストールされます。
gemのインストール結果を反映させるために、Railsアプリケーションを再起動します。
画像のサイズを変換する
画像のサイズを変換するには、variantのresizeを使います。
blobオブジェクト.variant(resize: "横幅x縦幅")
実例
<%= image_tag image, size: '300x200' %>
↓画像自体のサイズを変更
<%= image_tag image.variant(resize: "300x200") %>
※サイズは横幅のサイズに合わせて縦幅も自動調整されます。
デフォルトでは640×400のサイズだったものが、variantのresizeで変換後は300×188になります。
↓ variantで変換後の画像
複数のオプションを設定する
画像のリサイズ以外にも、回転、反転、グレースケールなど様々なオプションが用意されています。
例えば以下のようにすれば、30度回転させたグレースケールの画像を表示することができます。
<%= image_tag image.variant(resize: '300x200', rotate:30, type: :grayscale) %>
variantのその他の便利なオプション
variantにはほかにも切り抜きや、画像のサイズに合わせて縮小や拡大をするか判定するなど様々なオプションが用意されています。
variantの詳細については下記をご参考ください。