suin.io

Rails: carrierwaveの後継、refileでファイルアップロードを実装する

suin2016年9月11日

Railsのフォームにファイルアップロードを付けるときに便利なのがrefileです。ファイルアップロード系のgemといえばcarrierwaveが広く知られていますが、refileはcarrierwaveの作者がその後継として作ったもので比較的新しいものです。refileの特徴には

  • ファイルのアップロード先をファイルシステム・S3など設定できる
  • 簡単にORMに組み込める
  • 画像のサムネイルを生成できる

があります。ここではrefileを使ったファイルアップロードの実装手順を紹介します。

作ってみるもの

今回はシンプルに画像がアップロード出来るだけのフォームを実装していきます。

ここで作成した成果物のコードはGitHubで公開しています。

refileのインストール

Gemfileに追加します:

Gemfile
gem "refile", require: "refile/rails"
gem "refile-mini_magick"

追加したらインストールしRailsサーバを再起動します。

bundle

Imagemagickをインストールします:

brew install imagemagick # OS X
sudo apt-get install imagemagick # Ubuntu

ファイルアップロードを実装する

まずPhotoモデルを作ります。ファイルを持つモデルにはファイル名を格納するカラムが1つ必要です。そのカラムは_idで終わらないといけない命名規則があります。ここではfile_idとします。

rails generate scaffold Photo name:string file_id:string

file_idカラムにユニーク制約を付けるためにマイグレーションファイルを修正します:

db/migrate/20160911080000_create_photos.rb
class CreatePhotos < ActiveRecord::Migration
  def change
    create_table :photos do |t|
      t.string :name, null: false
      t.string :file_id, null: false

      t.timestamps null: false
    end
    add_index :photos, :file_id, unique: true
  end
end

データベースにマイグレーションをかけます:

rake db:migrate

次にattachmentメソッドをPhotoモデルに追加します。ここではファイルアップロードを必須にしたいのでvalidates_presence_of :fileをつけていますが、必須にする必要がなければこれを省いて下さい。

app/models/photo.rb
class Photo < ActiveRecord::Base
  validates_presence_of :name
  attachment :file
  validates_presence_of :file
end

PhotosControllerのstrong parametersにfileを追加します:

app/controllers/photos_controller.rb
class PhotosController < ApplicationController
  # ...
    def photo_params
      params.require(:photo).permit(:name, :file)
    end
end

ビューで画像を表示する箇所にattachment_image_tagを埋め込みます:

app/views/photos/index.html.erb
...
    <% @photos.each do |photo| %>
      <tr>
        <td><%= photo.name %></td>
        <td><%= attachment_image_tag photo, :file, :fill, 100, 100, format: 'jpeg' %></td>
        <td><%= link_to 'Show', photo %></td>
        <td><%= link_to 'Edit', edit_photo_path(photo) %></td>
        <td><%= link_to 'Destroy', photo, method: :delete, data: { confirm: 'Are you sure?' } %></td>
      </tr>
    <% end %>
...
app/views/photos/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <strong>Name:</strong>
  <%= @photo.name %>
</p>

<p>
  <strong>File:</strong>
  <%= attachment_image_tag @photo, :file, :fill, 300, 300, format: 'jpeg' %>
</p>
...

画像を投稿するフォームにファイル添付フィールドを付けます:

<%= form_for(@photo) do |f| %>
  <% if @photo.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2>

      <ul>
      <% @photo.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :file %><br>
    <%= f.attachment_field :file %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

以上でファイルアップロードの実装は完了です。

RELATED POSTS