<Ruby on Rails> 画像のアップロード

使用したgem

・carrierwave ファイルをアップロードする機能 github.com

・mini-magick 画像加工する機能 github.com

工程

1. carrierwaveとmini-magickをbundle install

#Gemfileに以下を追記し、bundle install
gem 'carrierwave', '~> 2.0'
gem 'mini_magick', '~> 4.8'


2. uploaderを作成

rails generate uploader 任意のファイル名

3. uploaderファイルに以下を設定

# class ファイル名+Uploader < CarrierWave::Uploader::Base

# アップロードしたファイルはpublic/配下に保存
storage :file

# public/配下のディレクトリの設定
def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end

# 画像がなかった時のdefault画像の設定
def default_url(*args)
  'board_placeholder.png'
end

# アップロードするファイルの拡張子を指定
def extension_whitelist
    %w(jpg jpeg gif png)
end

4. カラムに画像を追加

$ rails g migration add_ファイル名_to_テーブル名 カラム名:stirng
$ rails db:migrate

データベースサーバーの要領が圧迫されるため、カラムには'DBに保存されている画像ファイル名'が保存される。
画像を表示する時は
'画像の保存してある場所を示すpath’

'DBに保存されている画像ファイル名'
を使用して表示させる。 参考:pikawaka.com

5. form_withタグをviewファイルに記載

# <%= form_with model: モデル名, local: true do |f| %>
  <%= f.label :カラム名 %>
  <%= f.file_field :カラム名 %>
  <%= f.hidden_field :カラム名_cache %>
#   <%= f.submit %>
# <% end %>

6. controllerのstrongパラメータを画像データを入力できるように設定

def 〇〇_params
  params.require(:モデル名).permit(:カラム名, :カラム名, :画像カラム名, :画像カラム名_cache)
end

cacheとは?
Making uploads work across form redisplays
Often you'll notice that uploaded files disappear when a validation fails.
CarrierWave has a feature that makes it easy to remember the uploaded file even in that case.
Suppose your user model has an uploader mounted on avatar file, just add a hidden field called avatar_cache (don't forget to add it to the attr_accessible list as necessary).
- 公式documentより

検証が失敗すると、アップロードされたファイルが消えるため、
CarrierWaveにはその場合でもアップロードしたファイルを記録する機能があります。
カラム名_cacheという非表示フィールドを追加することで実装できます(必要に応じてattr_accessibleリストに追加すること)。

7. 出力対象の部分テンプレートに入力

# 部分テンプレートの画像挿入部分
# render @インスタンスのコレクション(例:@インスタンス名+s)で呼び出されている前提
  <%= image_tag ローカル変数.カラム名_url, size: '〇〇x〇〇' %>

8. .gitignoreにpublic/uploadsを追記

.ignoreにpublic/uploadsを追記することで、ローカルでアップロードされたファイルをgit管理下から外すことができる。

# .gitignore
public/uploads

詳しくはこちらを参照 【IT用語】.gitignoreの書き方を図解形式で理解しよう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト

選択した画像をプレビュー表示にする

1. 上記5で定義したf.file_fieldを以下に変更する。

# _form.html.erb(new.html.erbで
# <%= render 'form', { a: @b} %>を定義した場合
# a = 部分テンプレート内で使用する変数
# b = 変数にいれる値(controllerやviewで定義した変数名) 
<%= f.file_field :カラム名, onchange: 'previewImage()', accept: 'image/*' %>
<%= image_tag 変数a.カラム名.url, id: 'preview', size: '〇〇x〇〇' %>

・onchange:change イベントを処理する EventHandler 。入力欄や選択肢が変更された時に発生。
onchangeについて→GlobalEventHandlers.onchange - Web API | MDN
JavaScriptのonchangeの使い方を現役エンジニアが解説【初心者向け】 | TechAcademyマガジン

・acceptオプション:フォームで受付可能なMIMEタイプを指定
acceptオプションについて→フォーム(form) | Railsドキュメント
MIMEタイプについて→MIME タイプ (IANA メディアタイプ) - HTTP | MDN

2. assets/javascript/common.jsに以下を記載

function previewImage() {
    const target = this.event.target;
    const file = target.files[0];
    const reader  = new FileReader();
    reader.onloadend = function () {
        const preview = document.querySelector("#preview")
        if(preview) {
            preview.src = reader.result;
        }
    }
    if (file) {
        reader.readAsDataURL(file);
    }
}

FileReader オブジェクト:ユーザーのコンピュータに保存されているファイル (または生データ バッファ) の内容を非同期に読み取る。
FileまたはBlob オブジェクトを使用して、読み込むファイルまたはデータを指定します。 →FileReader - Web API | MDN