概要
ファイルのアップロードをGemのCarrierWave
で簡単にする手順。
CarrierWave
をインストールするUploader
を生成する- モデルのファイル名プロパティーと
Uploader
を関連付ける
あとはfile_field
から選択されたファイルをパラメーターに含むモデルでsave
、upload
など実行すれば、実体ファイルもそれに連動して操作される。
アップロードファイルのパラメーターが複数でも、1つのアップローダーで対応できる。
手順
要件
name
、image1
、image2
の3つのstring
属性を持つUser
モデルに画像をアップロードする- データベースのカラムもこれに対応した
VARCHAR
タイプの3つ
CarrierWaveの導入
Gemfileに以下の1行を追加。
1 |
gem 'carrierwave' |
Railsサーバーを止めてbundle install
後、サーバーを再起動
1 2 3 |
[vagrant@vagrant gem_test]$ rails g uploader user_image Running via Spring preloader in process 28455 create app/uploaders/user_image_uploader.rb |
Uploaderクラスの生成
Railsコマンドでアップローダークラスを生成する。この例ではUserImageUploader
としている。
1 2 3 |
[vagrant@vagrant uploads]$ rails generate uploader user_image Running via Spring preloader in process 2664 create app/uploaders/user_image_uploader.rb |
この操作によって、以下のディレクトリーとファイルが追加される。
app/uploaders/user_image_uploader.rb
モデルとDB
UserモデルとDBを結ぶマイグレーションファイルは以下のとおりで、画像ファイルのファイル名を保持するフィールドを2つ持っている。
1 2 3 4 5 6 7 8 9 10 11 |
class CreateUsers < ActiveRecord::Migration[5.1] def change create_table :users do |t| t.string :name t.string :image1 t.string :image2 t.timestamps end end end |
モデルパラメーターとの関連付け
モデルクラスのファイルにmount_uploader
の1行を追加し、パラメーターとアップローダーを関連付ける。ここではimage1
とimage2
にUserImageUploader
を関連付けている。
app/models/user.rb
1 2 3 4 |
class User < ApplicationRecord mount_uploader :image1, UserImageUploader mount_uploader :image2, UserImageUploader end |
コントローラー
コントローラーではアップローダーを特に意識する必要はない。
今回の例では、既にデータが登録されているusers
テーブルの先頭データを取り出し、画像パラメーターを更新している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class UsersController < ApplicationController def carrier_wave @user = User.first if params[:user].present? @user.update(user_params) end end private def user_params params.require(:user).permit(:name, :image1, :image2) end end |
ビュー
ビューでもCarrierWaveを意識する必要はない。ここでは画像更新機能として以下の2つを実行している。
file_field
で指定されたアップロード画像をコントローラーにPOST- アップロード画像が存在していれば表示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<div class="carrier_wave"> <main> <h1>CarrierWave</h1> <%= form_with(model: @user, url: carrier_wave_path, local: true) do |f| %> <%= f.text_field(:name) %> <%= f.file_field(:image1) %> <%= f.file_field(:image2) %> <%= submit_tag("登録") %> <% end %> <p><%= @user.name %></p> <% if @user.image1? %> <%= image_tag(@user.image1.url) %> <% end %> <% if @user.image2? %> <%= image_tag(@user.image2.url) %> <% end %> </main> </div> |
エラー対応
当初、NameError uninitialized-constantが発生。下記1行をapplication.rb
に追加してエラーは出なくなったが、なぜapp
下のuploaders
が自動で読み込まれないのか不明。
config/application.rb
1 2 3 4 5 6 |
module GemTest class Application < Rails::Application ..... config.autoload_paths += Dir[Rails.root.join('app', 'uploaders')] end endか |
ファイルの保存場所
デフォルトでは以下の場所に保存される。
- アップロードファイル
public/uplodads/モデル名/属性名/:id
- テンポラリーファイル
public/uploads/tmp
たとえば今回の例でid=1のユーザーのファイルは以下の様に保存される。ファイルを更新すると、前のファイルは削除される。
1 2 3 4 |
[vagrant@vagrant gem_test]$ ls public/uploads/user/image1/1 highway.jpg [vagrant@vagrant gem_test]$ ls public/uploads/user/image2/1 illumination.jpg |
各種設定
概要
ファイルの保存場所やファイル名の付け方などの設定は、rails g uploder
で生成したアップローダーのファイルに書かれている。
app/uploader/user_image_uploader.rb
1 2 3 |
class UserImageUploader < CarrierWave::Uploader::Base ..... end |
ファイル保存場所
媒体
storage
でローカルファイルへの保存を指定。fog
はクラウドサービス用のGem。
パス
def store_dir
でファイルを保存するディレクトリーのパスを設定。
1 2 3 4 5 6 7 8 9 |
# Choose what kind of storage to use for this uploader: storage :file # storage :fog # Override the directory where uploaded files will be stored. # This is a sensible default for uploaders that are meant to be mounted: def store_dir "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" end |
ファイル名
デフォルトではここはコメントアウトされていて、アップロード時のオリジナルファイル名が使われる。以下の様にコメントを外すと、ファイル名が全てsomething.jpg
に固定される。
1 2 3 4 5 |
# Override the filename of the uploaded files: # Avoid using model.id or version_name here, see uploader/store.rb for details. def filename "something.jpg" if original_filename end |
ここでランダム文字列などのUUIDを使うとユーザーごとにディレクトリーを分けなくてもよくなりそうだが、CarrierWave内でファイル種別を取得する必要がある(とりあえず先送り)。
ファイル拡張子
以下の部分のコメントを外すと、指定した拡張子のファイル以外はアップロードされず、データベース処理もロールバックされる。
1 2 3 4 5 |
# Add an allowlist of extensions which are allowed to be uploaded. # For images you might use something like this: def extension_allowlist %w(jpg jpeg gif png) end |
ただし明示的なエラーは発生しなかった。
1 2 3 4 5 6 7 8 |
Processing by UsersController#carrier_wave as HTML Parameters: {"utf8"=>"✓", ..., "user"=>{"name"=>"users1", "image2"=>...>}, "commit"=>"登録"} User Load (0.3ms) SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1 (0.1ms) BEGIN (0.2ms) ROLLBACK Rendering users/carrier_wave.html.erb within layouts/application Rendered users/carrier_wave.html.erb within layouts/application (1.2ms) Completed 200 OK in 17ms (Views: 9.5ms | ActiveRecord: 0.6ms) |