Programming Journal

学習したことの整理用です。

CarrierWaveを使って、掲示板に画像をアップロードする

今回、実装したいこと

  • 掲示板に画像を投稿したい。

  • 画像を登録する前に、プレビュー表示したい。

  • 画像の登録がない掲示板は、デフォルトの画像を表示する。

  • 登録できるファイルの種類を、jpg,jpeg,png,gifだけに制限したい。

  • 許可されている以外のファイルをアップロードしたときは、日本語でエラーメッセージを表示したい。

CarrierWaveをインストールする

CarrierWaveとは…画像をアップロードするGemです

以下の流れは、ほぼ公式に載っている内容です GitHub - carrierwaveuploader/carrierwave: Classier solution for file uploads for Rails, Sinatra and other Ruby web frameworks

先にImageMagickという画像処理ライブラリをインストールします。 (何に必要なのか正直理解できていないけど、これをインストールしろとerrorが出たので…)

$ brew install imagemagick
gem 'carrierwave'
gem 'rmagick', '~> 4.1', '>= 4.1.2' #画像のリサイズで使う

いつものように$ bundle installでOK

Boardモデルにboard_imageカラムを生成する

画像情報を格納するためのカラムを追加します。

$ rails g migration add_column_board_image_to_boards board_image:string

※migrationファイル名は迷う…今度正しい表記の方法を調べる

class AddColumnBoardImageToBoard < ActiveRecord::Migration[5.2]
  def change
    add_column :boards, :board_image, :string
  end
end

画像なしでも登録できるようにしたいので、NOTNULL制約はつけません。

アップローダーを生成する

Start off by generating an uploader!

まず、アップローダーを生成します。

ここに、デフォルト画像設定や登録できる画像のフォーマットの指定の設定などを記入していきます。

$ rails generate uploader image(アップローダー名)

※ imageっていう名前にしちゃったけど、board_imageカラムに合わせたほうがよかったかも…

モデルにアップローダーをマウントする

モデルファイルを開けてアップローダーをマウントします。

「マウントする」って日本語訳すると、はめ込むっていうのがわかりやすいのかな?

class Board < ApplicationRecord
  mount_uploader :board_image, ImageUploader

アップローダーファイルの編集

登録できるファイルの種類を制限する&デフォルト画像の指定をしていきます。

予め、コードが準備されてコメントアウトされているので、使いたいものはコメント化を解除していきます。

まず、こちらのコメントを解除。 RMagick使うよってこと。これを外しておかないと、後々、undefined method 'resize_to_fill'とエラーになりました。

RMagickを使わないとリサイズできないんですかね。

class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick

デフォルトで使う画像を指定していきます。

画像はapp/assets/imagesに格納しているので、ファイル名だけでOK

  # Provide a default URL as a default if there hasn't been a file uploaded:
  def default_url
    'board_placeholder.png'
  end

危険なスクリプトファイル等が登録されないようにするため、

whitelist(a list of people or things considered to be acceptable or trustworthy.)を特定しておきます。 whitelistって、信用に足りるor許可できること・人ってことか

余談ですが、Cromeの拡張機能Google Dictionaryを登録しておくとダブルクリックで英英訳が出てくるので、 英語の学習にもなります。英和より英英のほうがニュアンスがわかりやすい◎

  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  def extension_whitelist
    %w[jpg jpeg gif png]
  end
end

掲示板入力フォームを修正

画像を投稿する部分を加えていきます。 ここから難しかった・・・ 画像を登録する前にプレビュー表示させるには、JavaScriptが必要なんですね これを知らずに1時間くらい悩みました

参考にしたもの

Best way to show image previews before upload in rails & carrierwave - Stack Overflow

https://kurose.me/carrierwave-preview/

〜
<div class="form-group">
    <%= f.label :board_image %>
    <%= f.file_field :board_image, onchange: 'previewFileWithId(preview)', class: 'form-control mb-3', accept: 'image/*' %>
    <%= f.hidden_field :board_image_cache %>
  </div>
  <div class='mt-3 mb-3'>
    <%= image_tag board.board_image.url,
                  id: 'preview',
                  size: '300x200' %>
  </div>
〜

onchangeでイベントを指定

onchangeとは…要素の値が変わったときにonchangeイベントが発動する(日本語難しい)

The onchange event occurs when the value of an element has been changed.

For radiobuttons and checkboxes, the onchange event occurs when the checked state has been changed.

onchange Event

GlobalEventHandlers.onchange - Web API | MDN

acceptでファイルの種類を指定。image/*は画像ファイル全般。例えば、video/*は動画ファイル全般。

id: 'prevewJavaScriptで使用するため、idを設定しておきます。

<%= f.hidden_field :board_image_cache %>

ここの部分は、バリデーションに通らず掲示板登録ができなかったときに、画像を保持しておく機能

hidden_fieldに〇〇_cacheを指定しておきます

JavaScriptファイル作成

application.jsではなく、専用のファイルを作成します。

application.jsは、個別のCSSJavaScriptファイルをどう管理していくか記述していくファイル(マニフェストファイル)なので、コードを記載しません。

ファイル内のコメントでもこの記載があった。> It's not advisable to add code directly here

function previewFileWithId(id) {
  const target = this.event.target;
  const file = target.files[0];
  const reader  = new FileReader();
  reader.onloadend = function () {
      preview.src = reader.result;
  }
  if (file) {
      reader.readAsDataURL(file);
  } else {
      preview.src = '';
  }
}

掲示板一覧表示ビューを修正

画像を表示させたい部分に追加

アップローダーでデフォルト画像を設定しているため、if else式で登録画像・デフォルト画像かの分岐処理はいらないです。

'board.board_image.url' BoardモデルのBoard_imageからurlを引っ張ってきている

<%= image_tag board.board_image.url, class:'card-img-top', size:'300x200' %>
コントローラーのストロングパラメーターに追記
private

  def board_params
    params.require(:board).permit(:title, :body, :board_image, :bord_image_cache)
  end
エラーメッセージを日本語対応にする

公式に日本語訳が用意されているのでコピペして、'config/locales/carrierwave/ja.yml'に貼り付けました

carrierwave-i18n/ja.yml at master · carrierwaveuploader/carrierwave-i18n · GitHub

ローカル環境でテスト用に登録した画像がリモート環境へアップロードしないように設定する

これは失念してました… すでにリモートへpushした後だったので、 ターミナルでgit rm --cached ○○(ファイル名)でファイルをgit の管理外にする

面倒臭かったので、今後は注意します。

# Ignore vendor
/public/uploads
% git rm --cached public/uploads/board/board_image/ファイル名