CarrierWaveを利用した画像アップロード機能をRailsに実装しサムネイルとかに使う方法まとめ

Ruby on Railsチュートリアル拡張版の第一弾!Railsチュートリアルでは投稿に直接画像を追加するというロジックなのですが、WordPressのように画像は画像ボックスに保存して、保存した画像一覧からサムネイルを選んで記事を保存するのを作りたいと思いまして。今回は作成したメモをRailsチュートリアルみたいにまとめてみました!

CarrierWaveを利用した画像アップロード機能をRailsに実装しサムネイルとかに使う方法まとめ

本記事で実装する画像アップロード機能のゴール

Railsチュートリアルや他の画像アップローダを紹介するサイトでは、元々作成中のモデルの属性に画像カラムを追加していく方法で説明されています。

本記事では、WordPressのような画像投稿機能を実現するために、画像保存専用のテーブルを作成していくことを目標にします。

そして、最終的には画像一覧から選択して別モデル(Articleモデル)にサムネイルを保存するまでを実装します。

今回作成する機能の具体的なユーザフローは以下のようになっています。

  1. ユーザが画像投稿フォームにて画像、タイトル、代替テキストを送信
  2. 画像一覧画面から画像を選択してArticleにサムネイルのURLを保存

今回作成するアップローダの仕様を以下にまとめます。

  • 画像保存専用のモデル Pictureを作成
  • ユーザが画像をCarrierWaveのアップローダで保存
  • 画像をアップロードするフォームを作成
    • 画像のタイトル、代替テキスト、ファイルを保存
  • 画像のバリデーション
  • 画像のリサイズ、サムネイル作成
  • 画像のサムネイルを選択し、Articleモデルに保存

ロジック部分をメインに実装していくので、みためなどのCSSに本記事ではこだわりません。
見た目部分は雑な作りになっているので、適宜修正してください。

また、メモ書きを無理矢理記事にしているため、説明不足な部分が多いです。
随時、加筆修正していくので、質問・コメント等宜しくお願いします。

本記事のアップローダーを実装するために必要な事前準備

Railsチュートリアルを拡張する形で進めていきます。本記事に必要な事前準備を以下に記します。

  • できればRailsチュートリアル第11章まで進める
    • 進めたことが無い方は、完成形をcloneしてしまうのが早いかもしれません
    • 諸事情によりMicropostモデルをArticleモデルとして作成しています
  • もしくはUserモデルとArticleモデルを作成する
    • UserモデルとArticleモデルさえあれば動くので、手っ取り早く試してみたい方はこちらで

本記事で利用するファイルアップロード用のgem CarrierWave

Ruby on Railsでファイルアップロードを実装するには、PaperclipCarrierWaveという2つのgemがよく使われます。
2つのGemの違いは、

  • Paperclipは機能がシンプル
  • CarrierWaveは機能が豊富で応用が効く

です。

「rails 画像 アップロード」で調べたところ、CarrierWaveを紹介している記事が多いです。(2016年8月14日現在)
また、CarrierWaveが「Railsチュートリアル」や「パーフェクトRuby on Rails」で紹介されていることから、今回はCarrierWaveを使うことに決めました。
Railsチュートリアルを参考に拡張していくイメージで実装していきます。

CarrierWaveを利用した画像アップロード機能の実装手順

Railsチュートリアル第11章のマイクロポストを真似てピクチャを作成していきます。
ピクチャモデルを作成した後、CarrierWaveを仕様し画像のアップロード機能を実装します。
最後に、mini-magickを利用してアップロードされた画像を選択してサムネイルを記事に設定する機能を作ります。

  1. Pictureのモデルまわりを実装
    • Pictureのバリデーション・デフォルトスコープ
    • PictureとUserのリレーション定義
  2. Pictureのコントローラまわりを実装
  3. CarrierWaveまわり
    • CarrierWaveのUploaderクラスを生成
    • Viewにフォームを作成
    • バリデーションを設定
    • 画像アップロード部分のテスト
  4. MiniMagickまわり
  5. ピクチャリストからサムネイル画像を選択

1. Pictureのモデルまわりを実装

Railsチュートリアル第11章のmicropostモデルを真似しながら、pictureモデルを実装していく

以下のコマンドでPicutreモデルを生成

Pictureのマイグレーションファイルdb/migrate/[timestamp]_create_pictures.rbにインデックスを付与する記述を追記

マイグレーションを叩く

PicutreモデルのバリデーションとUserモデルとのアソシエーションを定義

Pictureのモデルにuser_idのバリデーションを追記

UserのモデルにPictureを複数所有する(hasmany)関連付け

ユーザが削除された時にPictureも削除されるように依存関係を記述する

Picture用のfixtureを追記

Picutreモデルの有効性テストをtest/models/picture_test.rbに書く

Userモデルのテストをtest/models/user_test.rbに追記

2. Pictureのコントローラまわりを実装

users/:user_id/picutresにアクセスすると、画像の一覧を表示
さらに、ピクチャのCRUDを記述していく

ホーム画面にピクチャ一覧をページネーション表示

Pictureのコントローラをコマンドで生成する

Userコントローラーにpicturesを記述

1つのPictureを表示するパーシャル

user_picturesにアクセスするためのルーティング

サンプルデータdb/seeds.rbにPictureを追加

シードデータをリセット

ユーザのピクチャ一覧画面におけるテスト

ユーザと関連付けされたピクチャのfixture
test/fixtures/pictures.yml

ユーザのピクチャ一覧画面におけるテスト
test/integration/user_pictures_test.rb

ピクチャのルーティング設定・アクセス制御

logged_in_userメソッドをApplicationコントローラに移す
app/controllers/application_controller.rb

Usersコントローラからlogged_in_userメソッドは消しておく

Pictureコントローラに各アクションを追記

各アクションのルーティングをテスト

ピクチャの作成 new/create

Picturesコントローラのnewcreateアクションを記述

Pictureのnewにピクチャ投稿フォームを作成
app/views/pictures/new.html.erb

app/views/shared/_picture_form.html.erb

ピクチャの削除 destroy

ピクチャのパーシャルに削除リンクを追加
app/views/pictures/_picture.html.erb

Picturesコントローラのdestroyアクション

fixturesに別のユーザに所属しているピクチャを追加

間違ったユーザによるピクチャの削除をテスト

ピクチャの更新 edit/update

ピクチャのパーシャルに編集リンクを追加

Picturesコントローラのeditupdateアクションを追記

Picturesコントローラのテスト

ピクチャまわりの統合テスト

ピクチャまわりの統合テストを生成

ピクチャのUIに対する統合テスト

3. CarrierWaveまわり

画像アップロード用のGemであるCarrierWaveを使って、画像アップロード機能を実装していく
Railsチュートリアル第11章まわりとほぼ同じである

gemにCarrierWaveを追加する

Gemfileにcarrierwaveを追加して、bundle installコマンドを叩いてgemをインストール
Railsチュートリアルだと、0.10.0を指定してインストールするが、0.10.0にはバグがある
今回は、0.11.0をインストール

CarrierWaveのUploaderクラスを生成する

CarrierWaveのジェネレーターでPictureアップローダーを作成する

Pictureモデルにfileカラムを追加するためマイグレーション生成

Pictureモデルにfileカラムを追加し、Pictureアップローダを設定

ここで一旦Rail serverを再起動

Viewに画像アップロードフォームを追記

ピクチャフォームに画像アップローダーを追加
app/views/shared/_picture_form.html.erb

fileを許可するparamsのリストに追加

ピクチャのパーシャルに画像を追加
app/views/pictures/_picture.html.erb

アップローダのバリデーションを設定する

Carrierwaveはデフォルトでは、アップロードされた画像に対する制限がないなどいくつかの欠点がある。
欠点を直すためにバリデーションを設定していく必要がある。

画像フォーマットのバリデーション

app/uploaders/picture_uploader.rbに追記

画像サイズに対するバリデーション

ファイルサイズに対するバリデーションはデフォルトのRailsにはない
独自のバリデーションをPictureモデルに定義する

フロント側でのバリデーション制御

大きすぎるファイルのバリデーションは、バックエンドだけでなくフロントエンドでも行う
そうすることで、長過ぎるアップロード時間を防いだり、サーバーの負荷を軽減できる

ファイルサイズとファイルの拡張子をjQueryでチェックする

.gitignoreファイルに画像用ディレクトリを追記

画像アップロード部分のテスト

今回のテストの要件を以下にまとめます。

  • 画像が添付されていない場合は、ピクチャを保存しない
  • タイトルや代替テキストは空でもOK
ピクチャモデルのテスト

まず、サンプル画像をfixtureディレクトリに追加する

画像アップロードがなかった場合バリデーションエラーとするロジックを追記してテスト
ここのテストが通らず多くの時間を費やしてしまいました。
何が起きていたかといいますと、
ピクチャモデルのテストに

と書いていたのですが、これだとなぜか@picture.filenilで上書きできていませんでした。
そこで調べてみると、以下の本家ドキュメントで解決策を見つけることができました。

If you want to remove the file manually, you can call remove_avatar!

とのこと

carrierwaveuploader/carrierwave: Classier solution for file uploads for Rails, Sinatra and other Ruby web frameworks

これを参考に、

としたら、テストが通りました。

もう一つなかなか解決できなかったのが、fixture_file_upload関数がモデルのテストだと動かないことです。
Railsチュートリアル第11章の画像アップローダのテストでfixture_file_uploadメソッドが使われています。同じやり方でモデルでもテストできると思ったのですが、以下のエラーが発生。

調べてみると、統合テストはActionDipatch::TestProcessを継承しているから、fixture_file_uploadメソッドが使えることが判明しました。したがって、モデルでfixture_file_uploadメソッドを使うためには、以下のコードをfixture_file_uploadメソッドを呼ぶ前に追記する必要があります。

以上の2点を踏まえて、追記したピクチャモデルのテストコードは以下になります。

また、ピクチャモデルのバリデーションを変更したので、今までOKだったユーザモデルのバリデーションが通らなくなったりします。その場合、適宜テストが通るように修正してください。

ピクチャの統合テストに画像アップロード部分を追記する

追記したピクチャの統合テストは以下になります。

4. MiniMagickまわり

CarrierWaveでは画像をアップロードの部分までしか実装できません。
MiniMagickというGemを使って、画像をアップロードした際に自動的にサムネイル画像を生成する機能を実装していきます

画像のリサイズ

前準備として、ImageMagickという画像を操作するためのパッケージが必要

MacでImageMagickをインストール

Homebrewを使うのが簡単

UbuntuなどのDebian系OSでImageMagickをインストール
MiniMagickのインストール

MiniMagickというGemを使って、CarrierWaveからImageMagickを使えるようにする

Gemfileにmini_magickを追加して、bundle installコマンドを叩いてgemをインストール

CarrierWaveのアップローダにリサイズを設定

アップロードすると最大1280×720にリサイズされる
リサイズに加え、100×100のサムネイルも生成する

ピクチャのパーシャルをサムネイルに変更

app/views/picutres/_picture.html.erbを変更

TODO MiniMagickまわりのテスト

MiniMagickまわりのテストを書いていません。
今後、追加していきたいと思います。

5. ピクチャリストからサムネイル画像を選択

サムネイル画像を選択ボタンを押すと、画像リストが出現
will_paginateを使い、動的に「もっと見る」を実装
サムネイル画像を選択すると記事のpicture_idにセットされるように

記事の作成と編集フォームにサムネイル選択ボタンを追加

記事の作成フォームapp/view/articles/new.html.erb

記事の編集フォームapp/view/articles/edit.html.erb
のサイドバーに以下を追記。

app/views/shared/_user_pictures.html.erbを作成してサムネイル選択ボタンを追記

サムネイル一覧を取得するためのAjax通信部分

Pictureコントローラのindexに以下を追加

Ajaxでpictures#indexにアクセスしたときに返すHTMLをapp/views/picutures/index.js.erbに書く

サムネイル一覧を表示する部分をapp/views/shared/_user_pictures.html.erbに追記

サムネイルをさらに取得するためのボタンをapp/views/shared/_user_pitcures_more.html.erbに追記

記事にサムネイルカラムを追加

サムネイルとクリックすると記事フォームのthumbにpicture.file.thumb.urlがセットされるように

app/views/shared/_article_form.html.erbのフォームにthumbを追加する。

app/views/article/new.html.erbapp/views/article/edit.html.erbに以下のJavaScriptを追記

app/controller/articles_controller.rbarticle_paramsthumbを追加

Viewのもろもろを変更

ユーザの記事一覧画面にサムネイルを表示させ、タイトルをクリックすると記事ページヘ

記事のshow画面でタイトルとコンテンツを表示

記事周りのテストを追加

記事モデルのバリデーションを追加しテスト
サムネイルを必須項目にする

テストから書く

thumbを必須項目にすると今までのテストでエラーが出るのでそれは適当に修正していく

本記事のまとめ

本記事では、WordPressのような画像投稿機能を実現する方法をRailsチュートリアルのようにまとめてみました。

今回作成した具体的な機能は以下のようになっています。

  • 画像保存専用のモデル Pictureを作成
  • ユーザが画像をCarrierWaveのアップローダで保存
  • 画像をアップロードするフォームを作成
    • 画像のタイトル、代替テキスト、ファイルを保存
  • 画像のバリデーション
  • 画像のリサイズ、サムネイル作成
  • 画像のサムネイルを選択し、ArticleモデルにURLを保存

ロジック部分をメインに実装していくので、みためなどのCSSをほとんど書きませんでした。
見た目部分は雑な作りになっているので、適宜修正してください。

また、最初にも書きましたがメモ書きを無理矢理記事にしているため、説明不足な部分が多いです。
随時、加筆修正していくので、質問・コメント等宜しくお願いします。

本記事の参考サイト

基本的にRailsチュートリアルの第11章を参考にしました。
第11章ユーザーのマイクロポスト | Rails チュートリアル

CarrierWave関連の参考

RailsのファイルをアップロードするgemのCarrierWaveのインストール方法 – Rails Webook
CarrierWaveを利用した画像ファイルのアップロード – blog.beaglesoft.net

mini_magick関連の参考

Carrierwaveで画像をリサイズする – 49hack
CarrierWave + RMagick 画像のリサイズをまとめてみました – 麺処 まつば
RMagick で正方形のサムネイルを作成する – Qiita

will_paginate関連の参考

やればできる子!will_paginateで動的「もっと見る」を実装 – 思い付くまでタイトル未定


もしよければ応援クリックお願いします
↓↓↓↓↓
にほんブログ村 IT技術ブログ IT技術メモへ