【Rails7】ActiveStorageの使い方


<br>

## はじめに
ECサイトを制作する課題に取り組んでいる際にActiveStorageについて調べたのでまとめました。  

<br>

## ActiveStorageとは
>Active Storageは、ストレージサービスへのファイルのアップロードや、ファイルをActive Recordオブジェクトにアタッチする機能を提供します。
[Active Storage の概要 - Railsガイド](URL "https://railsguides.jp/active_storage_overview.html#has-one-attached")

<br>

## ActiveStorageでできること


#### アップロードされたファイルをアタッチする

アップロードされたファイルをActiveRecordオブジェクトにアタッチさせることで、実際のテーブルにファイルのカラムがあるかのように扱うことができます。

#### itemsテーブル

| name | price | description |
|------|-------|-------------|

上記のようなテーブルにアップロードされた画像をimage属性としてアタッチし


| name | price | description | image |
|------|-------|-------------|-------------|

のような扱い方ができるようになり、
item.nameなどと同じようにitem.imageでattachさせた画像を表示させられるようになります。  


 

#### ストレージに保存する

アップロードされたファイルの保存先として
Railsアプリ内（ローカル）の他にもクラウドストレージサービスなどを指定することができ、
画像投稿機能があるサイトなど、利用されていくごとにファイルが増えていくような場合はそのようなサービスと連携させて保存していくのが良さそうです。


#### その他
アップロードされたファイルの「ActiveRecordオブジェクトへのアタッチ」「ストレージへの保存」以外にvariantなどを利用して画像の変形などを行いたい場合には、別途gemのインストールが必要になります。

<br>



## ActiveStorageの使い方
<br>

### 保存先の設定

``config/storage.yml``内に
```yaml:storage.yaml

test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

amazon:
  service: S3
  access_key_id:     <%= ENV['AWS_ACCESS_KEY'] %>
  secret_access_key: <%= ENV['AWS_SECRET_KEY'] %>
  region:            <%= ENV['AWS_REGION'] %>
  bucket:            <%= ENV['AWS_BUCKET'] %>
  
```
のような記述があり、ここがアップロードされたファイルの保存先を指定しています。

ここに記述されている``:test``,``:local``,``:amazon``の設定を別途``production.rb``等の環境ごとの設定ファイルに指定することで利用できます。

（開発環境では:localに、本番環境では:amazonなどのクラウドストレージを利用するやり方が良いかと思われます）
開発環境の設定``development.rb``では:localが保存先としてデフォルトで指定されています。
``config/environments/``の
```ruby:development.rb
Rails.application.configure do
  config.active_storage.service = :local
end
```
本番環境でクラウドストレージを利用する場合は``production.rb``で
```ruby:production.rb
Rails.application.configure do
  config.active_storage.service = :amazon
end
```
のように自身が用意したものに変更します。
（AWSなどのクラウドストレージに保存する場合は、認証情報を安全に管理するためにdotenv-railsなどのgemの利用が良さそうです。）
<br>
<br>
ここまで終わりましたら
<br>

``$ rails active_storage:install``
と
``$ rails db:migrate``
を実行します。
<br>
これでactive_storageを利用するための以下の３つのテーブルが作成されます。

##### active_storage_blobs
アップロードされたファイルのメタデータが保存されるテーブル
##### active_storage_attachments
blobとアタッチ対象のテーブルを結びつける中間テーブル
と
##### active_storage_variant_records　
variantを利用して生成されたデータが保存されるテーブル
<br>
<br>

### モデルとファイルの結びつけ
<br>
次に``app/models/○○.rb``にファイルとモデルを関連づけるように記述をします
<br>

一つのレコードが一つの画像を持つ場合
```ruby:item.rb
class Item < ApplicationRecord
  has_one_attached :image
end
```
のように記述します。
<br>
一つのレコードが複数の画像を持つ場合は
``has_many_attached :images``
のように記述します。
<br>
``:image``は実際に取り出すときに
user.imageのように記述する部分になるので自由に命名して大丈夫です。
<br>

### 登録フォームを作成する

viewに画像をアップロードできるフォームを作成します。

```erb:new.html.erb
<%= form_with model: @item, url: items_path, method: :post do |f| %>

<p>

<%= f.label(:name, "商品名") %>

<%= f.text_field :name %>

</p>

<p>

<%= f.label(:price, "値段 　") %>

<%= f.text_field :price %>

</p>

<p>

<%= f.label(:sku, "型番 　") %>

<%= f.text_field :sku %>

</p>

<p>

<%= f.label(:description, "説明　") %>

<%= f.text_area :description %>

</p>
### 他の属性と同じように書けばよい。
<%= f.file_field :image %>

<p>

<%= f.submit "作成" %>

</p>

<p>

<%= link_to "戻る", items_path %>

</p>

<% end %>
```
<br>

### コントローラーに記述


```ruby:items_controller.rb
class ItemsController < ApplicationController
  def create
    @item = Item.new(item_params)

    if @item.save
      flash[:success] = '商品を登録しました。'
      redirect_to admin_items_path
    else
      flash[:danger] = '商品の登録に失敗しました。もう一度試してください。'
      render 'new', status: :unprocessable_entity
    end
  end

  private

  ### :imageを追加
  def item_params
    params.require(:item).permit(:name, :price, :sku, :description, :image)
  end
end
```

### 表示させる

```erb:show.html.erb
<!-- items/show -->
<%= @item.name %>
<%= @item.price %>
<%= image_tag(@item.image) %>
```

<br>




### variantの使い方


<br>
variantを使って、画像のリサイズを行う方法は２種類用意されています。
<br>

- 画像処理ライブラリとしてImageMagickを利用する
- 画像処理ライブラリとしてvipsを利用する
<br>

>ImageMagickは、libvipsに比べて知名度が高く普及も進んでいます。しかしlibvipsは10倍高速かつメモリ消費も1/10です。JPEGファイルの場合、`libjpeg-dev`を`libjpeg-turbo-dev`に置き換えると[2〜7倍高速]になります。
[Active Storage の概要 - Railsガイド](URL "https://railsguides.jp/active_storage_overview.html#has-one-attached")

<br>
<br>
vipsを使ってみます。
<br>

<br>

### 使うための準備
<br>

#### vipsをインストールする

macなら

``brew install vips``または``libvips``

(vipsの中にlibvipsも含まれています。)
<br>

Dockerを利用している場合、Dockerfileにlibvipsが含まれているか確認してください。含まれていれば、別途インストールは不要です
<br>

#### image_processingを入れる
次にGemfileに
``gem "image_processing", ">= 1.2"``
を記述してbundle installをします。
<br>

image_processingを入れることで
Rubyでimage_magickを使うために必要な"Mini_magick"、Rubyでvipsを使うために必要な"ruby-vips"もインストールされます。
<br>

#### 補足
もしrails7以降にvariantでimage_magickを使う場合は
デフォルトの設定がvipsになっているため
``config/application.rb``に
```ruby:application.rb
config.active_storage.variant_processor = :mini_magick
```
の記述が必要なようです。(rails７以前の場合は反対にvipsを利用する場合に記述が必要)

<br>

#### 実際に使う

<br>



```erb:show.html.erb
<%= image_tag @item.image.variant(resize_to_fit: [150, 150]) %>
```
のように記述をする方法と
<br>

例えば、商品のモデル``app/models/item.rb``に
```ruby:item.rb
class Item < ApplicationRecord

has_one_attached :image

def thumnail

image.variant(resize_to_limit: [450, 300])

end

def large

image.variant(resize_to_limit: [600, 700])

end

end
```

のように定義しておいて、viewで
<br>

```erb:show.html.erb
<%= image_tag @item.thumnail %>
```
のように記述して使う方法があります。
<br>


#### variantの仕組み
最初にvariantが使用されているページにアクセスしたときに、
``rails active_storage:install``を実行した時に作成されたactive_storage_variant_recordsテーブルにvariantで生成された加工後のデータが保存されます。
次回以降にアクセスした際は新たに生成せず、そのデータを読み込んで利用するため、処理が早くなる。

#### 画像のリサイズにvariantを使うメリット
サーバー側でサイズの変更や加工などの画像処理をするためクライアント（ブラウザ）へ送信する画像のサイズを小さくすることができ、ページの読み込み速度を向上させられる。

ただし、画面の大きさに合わせて動的に画像サイズを変更できるわけではないのでCSSと合わせて使うのが良さそう。

<br>
<br>


### さいごに

自分なりに学んだことをまとめてみました。間違っている箇所などありましたら指摘いただけると幸いです。