30歳からのプログラミング

30歳無職から独学でプログラミングを開始した人間の記録。

Rails で画像のアップロード機能を実装する

CarrierWaveというライブラリを使うことで、簡単にRailsアプリに画像アップロード機能を付け加えることが出来る。
github.com

ここではCarrierWaveを使うための具体的な手順を書いていく。
動作確認はRuby 2.5.1Ruby on Rails 5.2.1macOS 10.13.6で行っている。

インストールとアップローダーの作成

まずはCarrierWaveをインストールする。

Gemfilegem 'carrierwave'追記して$ bundle installすれば、インストールされる。

すると、$ rails g uploader 作成するアップローダーの名前アップローダーというクラスを作れるようになる。

今回は$ rails g uploader imageとした。
そうするとapp/uploaders/image_uploader.rbが生成される。
これが設定ファイルのような役割を果たすが、取り敢えずは何も手を加えずに進めることにする。

モデルの作成

今回は題材として画像掲示板のようなものを作ってみる。
Postというモデルがあり、投稿者を表現するnameと画像を表現するimageというカラムを持つ。データ型はどちらもstring

早速モデルを作成する。マイグレーションも忘れずに行う。

$ rails g model Post name:string image:string
$ rails db:migrate

最後にapp/models/post.rbを次のように書けば、モデルの作成は完了。
imageカラムに、先程作成したアップローダーを紐付けるような感じだろうか?

class Post < ApplicationRecord
  mount_uploader :image, ImageUploader
end

たったこれだけで、画像を扱えるようになった。
あとは、画像だからといって特別なことは何も意識せず、Postモデルを使ってアプリを作ればいい。

コントローラーとフォームの作成

実際にアプリを作っていく。

まずconfig/routes.rbに以下を追記する。

  get '/posts', to: 'posts#show'
  post '/posts/create', to: 'posts#create'

/postsでこれまでの投稿一覧を確認することができ、そこに設置してあるフォームに入力した内容を/posts/createにポストする。

次はコントローラー。
$ rails g controller Postsで作成した後、次のように書く。

class PostsController < ApplicationController
  def show
    @posts = Post.all
  end

  def create
    @post = Post.new(permit_params)
    @post.save!
    redirect_to action: 'show'
  end

  private
    def permit_params
      params.require(:post).permit(:name, :image)
    end
end

画像アップロードなど意識していないことが分かる。
ただ単にフォームから送信されたものを受け取って保存しているだけで、特別な処理などない。

最後に、posts#showに対応したビュー、つまりapp/views/posts/show.html.erbを作成すれば完成。

<% @posts.each do |post| %>
  <p>
    <%= post.name %><br>
    <%= image_tag post.image.url %>
  </p>
<% end %>
<hr>
<%= form_for :post, url: 'posts/create'  do |f| %>
  <p><%=f.label "投稿者" %><br><%= f.text_field :name, placeholder: 'ハンドルネーム' %></p>
  <p><%=f.label "画像をアップロード" %><br><%= f.file_field :image %></p>
  <p><%= f.submit value: '投稿' %></p>
<% end %>

4行目のpost.image.urlでアップロードされた画像のURLを取得できることと、10行目のファイル選択ボックスで対象のオブジェクトをimageにしていることがポイント。

ファイルを選択した上で投稿をクリックすると画像がアップロードされ、画面上に表示される。

アップロードされた画像はpublic/uploads/post/image/:postのID/ファイル名に保存される。

バリデーション

上述のようにapp/uploaders/image_uploader.rbを編集することで様々な設定を行うことが出来る。

例えば、ファイルサイズによるバリデーション。
以下の記述を追加すると、5MB以下の画像のみが許可されるようになる。

  def size_range
    1..5.megabytes
  end

それ以上のファイルをアップロードしようとすると以下のエラーメッセージが出る。

Validation failed: Image File size should be less than 5 MB

サムネイル

rmagickというライブラリを組み合わせることで、画像のリサイズも出来る。
github.com

早速Gemfileにgem 'rmagick'追記して$ bundle installしようとしたが、エラーが出る。

自分の環境(macOS 10.13.6)だと、以下のエラーが出た。

An error occurred while installing rmagick (2.16.0), and Bundler cannot continue.

確認したところImageMagickが必要なようなので、Homebrewでインストールする。

$ brew install imagemagick@6
$ brew link --force imagemagick@6

その上で$ bundle installすると上手くいく。

続いてapp/uploaders/image_uploader.rbを編集していく。
画面上部の# include CarrierWave::RMagickコメントアウトを外した上で、以下を書く。

  version :thumb do
    process :resize_to_limit => [300, 300]
  end

これでアップロード時に、元画像とは別に300*300以内のサイズのサムネイル画像も保存されるようになる。
保存先は元画像と同じディレクトリで、thumb_アップロードした画像のファイル名という名前で保存される。

今回のデータ構造の場合、post.image.thumb.urlでアクセスできる。

<%= image_tag post.image.thumb.url %>

参考資料