😸

ActiveStorage あれこれ試す

2022/02/07に公開

Active Storage を試す

https://railsguides.jp/active_storage_overview.html

  • 上記を基に実際に触ってみる
    • 今回は AWS S3 を Storage として利用する
    • Ruby 3.0.3
    • Rails 6.1.4

前提

  • 唯一存在する Controller/Model 名は pages/page これを基に rails c などをしている
Rails.application.routes.draw do
  root 'pages#index'
  resources :pages
end

Command

Gem

gem 'aws-sdk-s3', '1.48', require: false

初期準備

  • master.key を基に暗号化される credentials.yml.enc
  • credentials を実行したタイミングで master.key が config 配下に生成されるようだ
EDITOR=vi rails credentials:edit
rails active_storage:install
rails db:migrate

AWS Key設定 ( EDITOR=vi rails credentials:edit の内容 )

aws:
  access_key_id: 使いたいS3のアクセス権限がある access_key
  secret_access_key: 使いたいS3のアクセス権限がある secret_key

Config ( storage.yml )

amazon:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: 使いたいS3が存在する Region #実際には .env とかに
  bucket: 使いたいS3の bucket name #実際には .env とかに

Config ( environments / development.rb )

  config.active_storage.service = :amazon
  # config.active_storage.service = :local

Attache を試す

View ( pages / index.html.erb )

  <div class="col-md-6 col-md-offset-3">
    <%= form_with model: @page, method: :post, url: pages_path, local: true do |f| %>

      <%= f.label :image %>
      <%= f.file_field :image, class: 'form-control' %>

      <%= f.submit "Upload", class: "btn btn-primary" %>
    <% end %>
  </div>
  <div class="col-md-6 col-md-offset-3">
    <% @page.each do | page | %>
      <img src ="https://shibano-test.s3.ap-northeast-1.amazonaws.com/<%= page.image.key if page.image.attached? %>">
    <% end %>
  </div>

Model ( page.rb )

class Page < ApplicationRecord
  has_one_attached :image
end

Controller ( pages_controller.rb )

  def create
    Page.create!(page_params)
    redirect_to root_path
  end

  private

  def page_params
    params.require(:page).permit( :id, :image)
  end

確認

なんの変哲もないただの初期画面

アップロードしてみた

  • が、S3直アクセスはS3側で public read 設定がないと Access Denied になる
  • 今回はお試しなので、手動で public read を付与した
  • サービスで利用する場合には Cloudfront + s3 や Web host として公開など。

Purge を試す(rasil console)

  • view から指定削除しようかとも思ったけども、、、そこまで試すのが億劫だったので rails c で軽く動作を確認する程度

  • ファイル実体は消える

  • acched? も false となる

[1] pry(main)> page = Page.last
  Page Load (2.1ms)  SELECT `pages`.* FROM `pages` ORDER BY `pages`.`id` DESC LIMIT 1
=> #<Page:0x00007f7f61b64c38 id: 1, created_at: Mon, 07 Feb 2022 12:38:22.339445000 UTC +00:00, updated_at: Mon, 07 Feb 2022 12:38:22.393025000 UTC +00:00, comment: nil>

[2] pry(main)> page.image.purge
  ActiveStorage::Attachment Load (3.6ms)  SELECT `active_storage_attachments`.* FROM `active_storage_attachments` WHERE `active_storage_attachments`.`record_id` = 1 AND `active_storage_attachments`.`record_type` = 'Page' AND `active_storage_attachments`.`name` = 'image' LIMIT 1
  TRANSACTION (3.8ms)  BEGIN
  ActiveStorage::Attachment Destroy (5.0ms)  DELETE FROM `active_storage_attachments` WHERE `active_storage_attachments`.`id` = 1
  Page Update (3.9ms)  UPDATE `pages` SET `pages`.`updated_at` = '2022-02-07 12:40:54.042033' WHERE `pages`.`id` = 1
  TRANSACTION (5.6ms)  COMMIT
  ActiveStorage::Blob Load (1.5ms)  SELECT `active_storage_blobs`.* FROM `active_storage_blobs` WHERE `active_storage_blobs`.`id` = 1 LIMIT 1
  TRANSACTION (1.6ms)  BEGIN
  ActiveStorage::Attachment Exists? (1.8ms)  SELECT 1 AS one FROM `active_storage_attachments` WHERE `active_storage_attachments`.`blob_id` = 1 LIMIT 1
  ActiveStorage::VariantRecord Load (1.9ms)  SELECT `active_storage_variant_records`.* FROM `active_storage_variant_records` WHERE `active_storage_variant_records`.`blob_id` = 1
  ActiveStorage::Attachment Load (2.5ms)  SELECT `active_storage_attachments`.* FROM `active_storage_attachments` WHERE `active_storage_attachments`.`record_id` = 1 AND `active_storage_attachments`.`record_type` = 'ActiveStorage::Blob' AND `active_storage_attachments`.`name` = 'preview_image' LIMIT 1
  ActiveStorage::Blob Destroy (3.2ms)  DELETE FROM `active_storage_blobs` WHERE `active_storage_blobs`.`id` = 1
  TRANSACTION (3.4ms)  COMMIT
  S3 Storage (167.8ms) Deleted file from key: yyn1zjlr1id7nigrlv3yyehy995p
  S3 Storage (66.0ms) Deleted files by key prefix: variants/yyn1zjlr1id7nigrlv3yyehy995p/
=> nil

[3] pry(main)> page.image.attached?
  ActiveStorage::Attachment Load (5.0ms)  SELECT `active_storage_attachments`.* FROM `active_storage_attachments` WHERE `active_storage_attachments`.`record_id` = 1 AND `active_storage_attachments`.`record_type` = 'Page' AND `active_storage_attachments`.`name` = 'image' LIMIT 1
=> false
[4] pry(main)> 
mysql> select * from active_storage_blobs;
Empty set (0.00 sec)

mysql> 
  • ファイル実体もちゃんと消えている

Tips ( 使いそうな物 )

s3 にあげた時のファイル名が知りたい

  • S3 の実体が乱数である事にお気づきであろうか。実際にどこになんの情報が確認されているのかを追ってみよう。
  • ファイル実体情報は下記 active_storage_blobs に格納されている。
  • filename は upload した時に渡された ファイル名
  • key が s3 に配置された時の ファイル名である
    • アップロードしてみた の画像キャプチャ参照
mysql> select * from active_storage_blobs;
+----+------------------------------+------------+--------------+-------------------------------------+--------------+-----------+--------------------------+---------------------+
| id | key                          | filename   | content_type | metadata                            | service_name | byte_size | checksum                 | created_at          |
+----+------------------------------+------------+--------------+-------------------------------------+--------------+-----------+--------------------------+---------------------+
|  3 | ldkba65mdfczd1u5ccoycz8hmrtb | amazon.png | image/png    | {"identified":true,"analyzed":true} | amazon       |      3846 | l+FlMT1sjISVAR5FaaJX2Q== | 2022-02-07 13:14:56 |
+----+------------------------------+------------+--------------+-------------------------------------+--------------+-----------+--------------------------+---------------------+
1 row in set (0.00 sec)

mysql> 
  • model との紐付けはこちら(多分...
    • record_id ...? page_id じゃないのは色々な Model が絡む可能性があるので?Model名は record_type に入る...?のかな。しらんけど
mysql> select * from active_storage_attachments;
+----+-------+-------------+-----------+---------+---------------------+
| id | name  | record_type | record_id | blob_id | created_at          |
+----+-------+-------------+-----------+---------+---------------------+
|  3 | image | Page        |         3 |       3 | 2022-02-07 13:14:56 |
+----+-------+-------------+-----------+---------+---------------------+
1 row in set (0.00 sec)
mysql> select * from pages where id = '3';
+----+----------------------------+----------------------------+---------+
| id | created_at                 | updated_at                 | comment |
+----+----------------------------+----------------------------+---------+
|  3 | 2022-02-07 13:14:56.486984 | 2022-02-07 13:14:56.528141 | NULL    |
+----+----------------------------+----------------------------+---------+
1 row in set (0.00 sec)
  • ファイル名を元に何かしたい場合には key を利用すれば良い
[2] pry(main)> page.image.key
  ActiveStorage::Attachment Load (4.7ms)  SELECT `active_storage_attachments`.* FROM `active_storage_attachments` WHERE `active_storage_attachments`.`record_id` = 3 AND `active_storage_attachments`.`record_type` = 'Page' AND `active_storage_attachments`.`name` = 'image' LIMIT 1
  ActiveStorage::Blob Load (1.5ms)  SELECT `active_storage_blobs`.* FROM `active_storage_blobs` WHERE `active_storage_blobs`.`id` = 3 LIMIT 1
=> "ldkba65mdfczd1u5ccoycz8hmrtb"
[3] pry(main)> 

商用で利用する時

配信まわり

  • 画像配信周りの S3 を切り出したい ( Imagekit とか画像圧縮専用サービスをかます時楽 )
  • もしくは静的ファイル専用の S3 を切り出す ( めっちゃキャッシュするよバケットで切り出す )
  • 1 domain 構成の場合には、PATH を指定しやすい形にしておくほうが良い。
  • .env と view の記載方法をあらかじめ分けて書いておきましょうね。

運用まわり

  • DB レコード = bucket file の存在チェックが必要...?

Discussion