スポンサーリンク
概要
Ruby on Railsで5から追加されたActiveStorageを使ったファイルアップロード機能を実装した時のメモ
スポンサーリンク
前提
Mac OS 10.15.4
予めRailsの一式はインストール済
Ruby 2.6.3p62
Rails 6.0.2.2
スポンサーリンク
スポンサーリンク
手順
インストール
下記のコマンドでActiveStorageのインストールとDBのマイグレーションを実施する。
(ActiveStorageをインストールすると新しいテーブルが追加されるため、マイグレーションが必要となる。)
1 2 |
rails active_storage:install rails db:migrate |
AWSなどを使う場合にはこの後、設定が必要だがデフォルトのサーバのローカールストレージのまま使用する。
モデル設定
任意のモデルを作成する
rails g model Article title:string
作成したモデルにファイルアップロード用のフィールドを追加する。
"has_one_attached: フィールド名"で定義する。
複数のファイルをアップロードしたい場合は、has_many_attachedにする。
1 2 3 |
class Article < ApplicationRecord has_one_attached :title_image end |
追加後にDBのマイグレーションを実行する。
rails db:migrate
コントローラ設定
コントローラを作成する。
rails g controller Article
ストロングパラメータ(article_params)に画像用のフィールドを忘れずに追加する事。
destroyでデータ削除時には、purgeでファイル自体を削除する。
あとは特段意識せずに、通常と同じように実装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
class ArticleEditController < ApplicationController # GET /article/new def index @articles = Article.all end # GET /article/new def new @article = Article.new end # POST /article def create @article = Article.new(article_params) if @article.save // else // end end # PATCH/PUT /article/:id def update if @article.update(article_params) // else // end end # DELETE admin/article_edit/1 def destroy @article.title_image.purge if @article.title_image.attached? @article.destroy // end private def article_params params.require(:article).permit(:title, :title_image) # 画像用のフィールドも追加 end end |
View設定
コントローラに対応するビューを作成する。
新規作成時のViewを一例として取り上げる。
attachedで画像が以前にアップロード済かどうかを判断可能。
アップロード済の場合はimage_tagヘルパーで画像を表示する。
1 2 3 |
// views/article/new.html.erb <h1>記事編集(新規)</h1> <%= render 'form', article: @article %> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// views/article/_form.html.erb <%= form_with(model: article, local: true) do |form| %> // 略 <div class="field"> <%= form.label :title %> <%= form.text_field :title %> </div> <div class="field"> <%= form.label :title_image %> <%= form.file_field :title_image %> // アップロードした画像があれば表示する <%= if article.title_image.attached? %> <%= image_tag article.title_image %> <% end %> </div> <div class="actions"> <%= form.submit %> </div> <% end %> |
下記のように一度確定後に、再度編集を行うと下記のように以前の画像が表示される。
スポンサーリンク
View設定(プレビュー)
現在の実装では確定後の画像しか表示できないため、ファイル選択後にプレビューできるようにする。
ブラウザ側での処理となるのでJS側に処理を追加する。
jQueryの追加
DOM操作をしやすいようにjQueryをインストールして使う。
yarnでインストール
yarn add jquery
jsをバンドルできるように設定する。
app/assets/javascripts/application.js
// 追加
require('jquery')
webpackに追加
config/webpack/environment.js
1 2 3 4 5 6 7 8 9 10 11 12 |
const { environment } = require('@rails/webpacker') // 追加 const webpack = require('webpack') environment.plugins.append('Provide', new webpack.ProvidePlugin({ $: 'jquery', jQuery: 'jquery' }) ) module.exports = environment |
実装
ファイルダイアログで画像選択時に画像を追加する処理を定義する。
先ほどのフォームを編集する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<%= javascript_pack_tag 'article_edit/_form' %> <%= form_with(model: article, local: true) do <div class="field"> <%= form.label :title %> <%= form.text_field :title %> </div> <div class="field image_field"> <%= form.label :title_image %> <%= form.file_field :title_image %> // idを付与image_preview <div id="image_preview"> // アップロードした画像があれば表示する <%= if article.title_image.attached? %> <%= image_tag article.title_image %> <% end %> </div> </div> <div class="actions"> <%= form.submit %> </div> <% end %> |
下記のようなjsを定義する。
注意点として、"article_title_image"は
<%= form.file_field :title_image %>
で自動で付与されるidなので使用するフィールド名に合わせて変更する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$(document).on("turbolinks:load", function () { $("#article_title_image").on("change", function (e) { var files = e.target.files; var d = new $.Deferred().resolve(); $.each(files, function (i, file) { d = d.then(function () { return previewImage(file); }); }); }); var previewImage = function (imageFile) { var reader = new FileReader(); var img = new Image(); var def = $.Deferred(); reader.onload = function (e) { // 画像を表示 $("#image_preview").empty(); $("#image_preview").append(img); img.src = e.target.result; def.resolve(img); }; reader.readAsDataURL(imageFile); return def.promise(); }; }); |
その他
画像のリサイズ
アップロード後の画像をそのまま使うのではなく、リサイズや加工する事も可能。
GemFileの下記がデフォルトではコメントアウトされているので、外してbundle installを行う。
# Use Active Storage variant
gem 'image_processing', '~> 1.2'
ImageMagickがインストールされていない環境で使用する場合、brewやapt-getなどでインストールしておく。
表示時にvariantメソッドを呼び出して、表示サイズなどを指定する事でリサイズ後の画像が表示されます。
processedをつける事で、既に変換済の画像がある場合には再利用するのでキャッシュが効くようになり早くなります。
<%= image_tag article.title_image.variant(resize_to_fill: [300, 300]).processed %>
ファイルの削除
画像ファイルの差し替え時などには削除されずにファイルは残るでの下記の処理を定期的に実行する事で紐付けできていないファイルも消す事が可能な模様。
(まだ未検証)
ActiveStorage::Blob.unattached.find_each(&:purge)
バリデーション
下記のようにモデル内にバリデーションを実施する事でファイルのチェックも可能。
下記はファイルサイズのチェックを実施。
1 2 3 4 5 6 7 8 9 10 |
class Article < ActiveRecord::Base has_one_attached :title_image validate :image_size private def image_size errors.add :image, 'too big' if image.blob.byte_size > 4096 end end |
スポンサーリンク
まとめ
駆け足だが、ActiveStorageでのファイルアップロード方法を紹介した。
DB処理としてのActiveRecordは、SQLを隠蔽するため独自のルールを多く覚えないといけないが、
ActiveStorageはシンプルでファイルのアップロード操作が使いやすく隠蔽されていて素晴らしいと感じました。
下記で技術書半額セールの内容を紹介していますので、興味がある方は参照してみてください。