gem 'carrierwave' , 'mini_magick'を使用して画像投稿機能を実装してみた.。

はじめに

画像投稿機能を実装する際に初めて利用したgemなので、振り返りとしてまとめています。

環境

carrierwaveとは?

ファイルをアップロードする方法を提供してくれるgemです。
特徴としてはUploaderクラスを別に提供してくれるので、フォーマットの指定、画像のリサイズなどのロジックをモデルに書かなくて済みます。
その為、「この設定をこのモデルとこのモデルに適応する」といったことを柔軟に行えるような仕様になっています。

実装手順

今回はBoardsテーブルが事前にあり、そこにboard_image:stringを追加して作業を行っていくとします。

db/ schema.rb

  create_table "boards", force: :cascade do |t|
    t.string "title", null: false
    t.text "body", null: false
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "board_image"    # <= このカラムを利用していきます!!
    t.index ["user_id"], name: "index_boards_on_user_id"
  end

Uploaderクラスを作成

今回は「BoardImage」という名前で作成していきたいと思います。

rails g uploader BoardImage
# => uploaders/board_image_uploader.rbを作成してくれます。

デフォルトの挙動を確認する

作成されたファイルの中身を確認したいと思います。

  • storage :file
    public/に画像が保存されることになります。

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

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

カラムとUploaderを結びつける

今回はBoardsテーブルのboard_imageカラムと結び付けたいので

models/board.rb

class Board < ApplicationRecord
  mount_uploader :board_image, BoardImageUploader # <= 追加する
end

フォームの作成

今回は単一の画像投稿にしたいと思います。

  <%= f.label :board_image %>
  <%= f.file_field :board_image, id: 'board_board_image' %>

ここまでで画像をアプリ側に保存するまでは終わりです。

対応するカラムがなぜstring型なのか?

DBにはファイルそのものを入れるのではなく、ファイルを参照する為のデータを保存します。これにより、DBの圧迫を抑えることができます

保存した画像を表示する

UploaderではURL,pathなどを取得する為のメソッドが用意されています。

> board = Board.last
# =>  board_image: "sample.jpeg" が入っているとします
> board.board_image.file.nil?
# => false
> board.board_image.url
# => "/uploads/board/board_image/40/sample.jpeg"
> board.board_image.path
# =>"/Users/username/Desktop/sample/public/uploads/board/board_image/5/sample.jpeg"
> board.board_image_identifier
# => "sample.jpeg"

今回はimage_tagを使用して画像を表示したいと思います。

<%= image_tag board.board_image.url  %>

また、画像を保存していないデータの為にデフォルトのURLも指定することができます。

uploaders/board_image_uploader.rb

def default_url
    'default.png'   # <= app/assets/images/default.png を返す。
end

これによって、HTMLでデータ一覧を表示する際にif文を使用して画像データの有無に対しての処理を分けなくて済みます。

フォーマットのホワイトリストを指定する

以下のように指定できます。

uploaders/board_image_uploader.rb

def extension_whitelist
    %w[jpg jpeg gif png]
end

画像のリサイズを行う

  • mini_magickを使用するのでbundleします。

インストールしたら、Uploaderクラスに以下のコードを追加します。

uploaders/board_image_uploader.rb

class BoardImageUploader < CarrierWave::Uploader::Base
 
  include CarrierWave::MiniMagick  # <= デフォルトはコメントアウトされています。

  process resize_to_fill: [300, 200, "Center"]  # <= 追加します

end

これでリサイズが行われます。

他にもリサイズする為のメソッドはいくつかあるので試してみてください!!

おまけ プレビュー機能の追加

おまけとして以下のようなプレビュー機能を作成したいと思います。

f:id:matazoukun:20201022142455g:plain
preview機能

html

<div class="form-group">
  <%= f.label :board_image %>
  <%= f.file_field :board_image, id: 'board_board_image', class: 'form-control', accept: "image/jpeg, image/jpg, image/gif, image/png" %>
</div>

<!-- 以下が画像が表示される部分です -->
<div class="form-group">
  <%= image_tag 'board_placeholder', size: '300x200',id: 'preview_img' %>
</div>

Javascript

document.addEventListener('DOMContentLoaded', () => {
  const file_button = document.querySelector('#board_board_image');
  const preview_img = document.querySelector('#preview_img');

  file_button.addEventListener('change', (e) => {
    let file = e.target.files;
    let reader = new FileReader() ;
    reader.readAsDataURL(file[0])
    reader.onload = function() {
      preview_img.src = reader.result;
    }
    },false);
});

参考

https://rubydoc.info/gems/carrierwave/frames

GitHub - minimagick/minimagick: mini replacement for RMagick

【Rails】CarrierWaveチュートリアル | Pikawaka - ピカ1わかりやすいプログラミング用語サイト

JavaScriptでFile APIを用いたファイル操作方法ついて解説 | ELOOP(イーループ) - 開発課題に取り組んで身につける実践型プログラミング学習サービス