【Rails】ActiveStroageの使い方を実例で解説|複数画像やファイルのアップロードと個別に削除・変更する方法

rails-prograshi(プロぐらし)-kv Rails
記事内に広告が含まれていることがあります。
[PR]

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に関する情報を保存するテーブル
Blobとは?

Blobとは、DBのデータ型の一つで、画像や音声、圧縮ファイルなどの大きなデータを保存するためのものです。

Binary Large Object(大きなバイナリのオブジェクト)の略です。

バイナリとはコンピュータ用に2進数を使った表記のことです。バイナリファイルは人間が読むことはできない、コンピュータ専用のファイルです。

variantとは?

variantとは、RailsにおいてBlob型のデータに対して使えるメソッドの一つです。

variantを使うと、画像を呼び出したときに、ビューの中で画像のサイズを変換することができます。

<%= image_tag user.avatar.variant(resize: "100x100") %>

variantの画像の変換ツールにはMiniMagickというモジュールを使っています。

reseize以外にもrotate(回転)などのメソッドも用意されています。

(参考)Github MiniMagick


実例

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つのテーブルを生成する処理が記述されています。

  1. active_storage_blobs
  2. active_storage_attachments
  3. 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
あわせて読みたい

railsのscaffoldコマンドはDB操作に必要な一連のファイルを一気に生成してくれる超便利コマンドです。

それぞれのファイルが何を意味しているかを理解すると、カスタマイズができたり、より有効的な活用ができます。

scaffoldコマンドの詳細については下記をご参考ください。

(参考)超便利コマンドscaffoldの使い方を完全理解|何が起こっているかを徹底解説


モデルファイルの編集

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カラムのデータを受けとれるようになります。

あわせて読みたい

Rails4以降ストロングパラメータの設定は必須です。

ストロングパラメータはセキュリティのために、指定したカラムのデータしか受け付けないようにする処理です。

ストロングパラメータの詳細については下記をご参考ください。

(参考)超便利コマンドscaffoldの使い方を完全理解|ストロングパラメータとは?なぜ必要か


フォームの編集

ビューファイルを変更して、画像を添付する(: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 モデルのインスタンス.画像カラム名 %>
image_tagとは?

image_tagとはRailsのメソッドの1つで、画像を表示するために使います。

第1引数に画像のパスを指定することで、コンパイル後にimageタグのsrc属性に指定したファイルへの絶対パスが記載されます。

画像の配置場所は、app/assets/images配下か、app/public配下です。assets/images配下のときはファイルパスの冒頭に「/」はなし。app/public配下のときはファイルパスの冒頭に「/」を記述することで区別します。

ActiveStorageを使ったblobオブジェクトを呼び出す場合は、コンパイル後のsrc属性にActiveStorageのパスが記載されます。

id, class, altの指定

id, class, alt属性を指定することもできます。

<%= image_tag 'ファイルパス', alt: 'altの内容', id: 'id名', class: 'クラス名' %>

widthやheightの指定

表示するimgタグに付与するwidth属性とheight属性を指定することもできます。

指定方法は2つあります。

  1. heightとwidthを指定する
  2. sizeを使う
<%= image_tag 'ファイルパス', width: 横幅の数値, height: 高さの数値 %>

以下と同じです。

<%= image_tag 'ファイルパス', size: '横幅の数値x高さの数値' %>

※数値に単位は不要です(単位をつけるとエラーになります)
※heightとwidthはどちらか一方でも可能です


詳細ページの編集

画像を添付しない場合もあるので、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つのレコードに対し複数枚の画像を添付できるようにします。

変更内容

変更する内容は以下になります。

  1. モデルをhas_many_attachedにし、値を複数形のimagesにする。
  2. ストロングパラメータを配列 images: [] にする。
  3. フォームにmultiple: trueを追加し複数選択を許可する。
  4. 詳細表示ビューのeachメソッドを使って、複数枚表示できるようにする。
  5. 一覧表示ビューで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
注意点

値が配列でないときと書き方が変わるので注意してください。

OK images: []

NG :images
NG :images []


フォームの変更

デフォルトではフォームは複数選択ができない状態です。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の列に各ユーザーに添付した画像の枚数が表示されています。


画像表示と個別削除機能の実装

上記の状態では、編集ページで情報を更新するときに、画像をゼロから選び直さなければいけません。

ここでは、編集画面で選択済みの画像を表示して、個別に削除できる機能を追加します。

変更内容

  1. フォームで削除したい画像を選択可能にする
  2. updateアクションに画像を削除する処理を追加する
  3. 上書きしないよう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オプション{ オプション名:値,,, } の形で複数指定できます。
3checked_value = “1”チェックされたときに送信するvalueの値です。trueにすれば1が入ります。自分で数値を指定できます。(true or 数値)
4unchecked_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][]
3checked_value = “1”image.idvalueにeachメソッドで抜き出したblobオブジェクトのid番号が入ります。
4unchecked_value = “0”false選択されていない場合は何もしません。

(参考)Railsドキュメント check_box



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ファイルでスタイルを適用するのが推奨です)


ブラウザで確認

フォーム部分の見た目は次のようになります。画像をクリックしてもチェックボックスが反応します。

この状態では次の問題点があります。

  1. 画像の個別削除機能は実装されていない。
  2. 新しい画像を添付すると、既存の画像がすべてなくなる(上書きされる)


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>
tips

コントローラ内で使っている変数の中身を確認するには、ppメソッドを使うと便利です。

デバッグ結果は、Railsのバッシュ(ターミナル)に表示されます。他にも情報が多いので、第1引数でわかりやすい文字列をいれておくと探しやすくなります。

 def update
    pp "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", params[:user]
    ・
    ・
 end

 ↓ バッシュの表示


削除対象の画像があるか確認

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を使えば、ダウンロード処理が実行できます。

ダウンロード機能の実装手順

  1. ダウンロード用のルーティング作成
  2. ダウンロードアクションの作成
  3. ダウンロードリンクの設置

ダウンロード用のルーティング作成

まずはダウンロード用のルーティングを作成します。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
あわせて読みたい

resourcesを使ったルートに、独自のルートを追加するには、memberとcollectionという便利な機能があります。

詳細については下記をご参考ください。

(参考)resourcesにルートを追加する方法|memberとcollectionとは?違いと使い方を徹底解説


ダウンロードアクションの作成

続いて、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")
tips

文字列と変数を組み合わせるときは、ダブルクオテーションの中で#{変数}とします。(Rubyでは式展開と呼びます)

シングルクオテーションだと機能しないので注意してください。

(参考)Railsドキュメント send_data


ダウンロードリンクの設置

最後に、クリックした画像をダウンロードできるように、詳細ページの各画像にダウンロードリンクを設置します。

<% 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を渡しすための省略記法です。

あわせて読みたい

Rubyでハッシュ(連想配列)を指定するときの省略記法の成り立ちについては下記をご参考ください。

(参考)ハッシュ(連想配列)の省略した書き方|シンボルと文字列の違い

以上で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アプリケーションを再起動します。

tips

ActiveStorageのデフォルトの画像プロセッサにはMinMaginc(gem名 mini_magick)が使われています。

画像のサイズ変換など基本的な処理であればデフォルトのMinMagicで問題ありません。


画像のサイズを変換する

画像のサイズを変換するには、variantのresizeを使います。

blobオブジェクト.variant(resize: "横幅x縦幅")

実例

<%= image_tag image, size: '300x200' %>

↓画像自体のサイズを変更

<%= image_tag image.variant(resize: "300x200") %>

※サイズは横幅のサイズに合わせて縦幅も自動調整されます。


デフォルトでは640×400のサイズだったものが、variantのresizeで変換後は300×188になります。

↓ variantで変換後の画像

tips

variant(resize: "300x200")と指定した場合、横幅に合わせてアスペクト比が保たれます。強制的に、指定したサイズにしたい場合は、「!」を使います。

variant(resize: "300x200!")


複数のオプションを設定する

画像のリサイズ以外にも、回転、反転、グレースケールなど様々なオプションが用意されています。

例えば以下のようにすれば、30度回転させたグレースケールの画像を表示することができます。

<%= image_tag image.variant(resize: '300x200', rotate:30, type: :grayscale) %>

variantのその他の便利なオプション

variantにはほかにも切り抜きや、画像のサイズに合わせて縮小や拡大をするか判定するなど様々なオプションが用意されています。

variantの詳細については下記をご参考ください。




参考リンク

Railsガイド Active Storageの概要

タイトルとURLをコピーしました