掲示板に画像アップロード機能を実装

実装内容

  • 掲示板作成画面から画像を投稿する。
  • gem CarrierWaveとMiniMagickを利用。
  • サムネイルというラベルを追加し、画像の選択をする。
  • 画像を選択せずに投稿した場合、デフォルトの画像を表示。
  • アップロードできるファイルはjpg, jpeg, png, gifのみに制限。
  • 画像投稿時にプレビューとして表示。

    導入

    CarrierWaveRailsアプリケーションに画像アップロード機能をつけることができる。
    MiniMagickはアップロードされた画像のリサイズなどができる。
    MiniMagickを使用するには、ImageMagickが必要なので事前にインストールする。

$ brew install imagemagick
gem 'carrierwave'
gem 'mini_magick'

Boardモデルのboardsテーブルにboard_imageカラムを追加する。

board_imageカラムには画像情報(ファイルそのものではない)が格納される。

$ rails g migration AddBoardImageToBoards board_image:string
Running via Spring preloader in process 10390
      invoke  active_record
      create    db/migrate/20210520113137_add_board_image_to_boards.rb

画像を選択せずに掲示板を作成できるようにしたいので、マイグレーションファイルはそのままでNOTNULL制約はつけない

class AddBoardImageToBoards < ActiveRecord::Migration[5.2]
  def change
    add_column :boards, :board_image, :string
  end
end
$ rails db:migrate
== 20210520113137 AddBoardImageToBoards: migrating ============================
-- add_column(:boards, :board_image, :string)
   -> 0.0013s
== 20210520113137 AddBoardImageToBoards: migrated (0.0016s) ===================

画像uploaderの作成

ジェネレータでuploaderを生成することから始める。

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

これにより次のファイルが生成される。
ここには画像のリサイズなど、設定を記述する。

 create  app/uploaders/image_uploader.rb

uploaderとモデルを紐付ける。

今回はBoardモデルと紐付ける。

class Board < ApplicationRecord
  mount_uploader :board_image, ImageUploader

image_uploader.rbの編集

このファイルはデフォルトでコードが記述されており、コメントアウトを解除して使用する。 MiniMagickでリサイズするので記述。

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

デフォルトの設定

  • storage :file
    public/uploads/に画像が保存される。

  • def store_dir
    画像が保存される先のpathを指定。

  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

追加設定

  • 画像を選択せずに投稿した場合、デフォルトとして使う画像を指定する。
  • アップロードできるファイルはjpg, jpeg, png, gifのみに制限。
  def default_url
    'board_placeholder.png'
 # app/assets/images/board_placeholder.png
  end
 
  def extension_allowlist
    %w(jpg jpeg gif png)
  end

viewの作成

掲示板作成フォームを編集する。
画像投稿時にプレビューとして表示するには、画像はまだデータベースへ登録されていないので、選択した画像はjavascriptを使って表示させる必要がある。

#...
 <div class="form-group">
 <%= f.label :board_image %>
 <%= f.file_field :board_image, onchange: 'previewFile(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イベントハンドラでjavascriptを指定
フォームの入力値、選択が変更されたときに処理を行う。
acceptでファイルを指定する。image/
は画像ファイル、video/*は動画ファイル。

<%= f.hidden_field :board_image_cache %>
これは、掲示板作成できなかった場合、アップロードした画像を消えないようにする処理。

javascriptでプレビューを表示させる。

jsファイルの作成。
application.jsは個別のjavascriptを設定するファイルなので、ここには記述してはいけない。

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 = '';
    }
  }

掲示板一覧画面へアップロードした画像を表示させる。

board.rbの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, :board_image_cache) end end

ローカルでアップロードした画像をリモートへアップロードしないように設定。

.gitignoreに画像のアップロード先を追加する。

# Ignore vendor
/public/uploads
既にpushしてしまった場合
rmコマンドでリモートリポジトリから削除する。
 $ git rm -r --cached public/uploads
# public/uploadsディレクトリ下のファイルをすべて削除