われプログラミングする、ゆえにバグあり

私だって価値を創造してみたいのです

CarrierWave でファイルアップロード

Scaffold で作成した CRUD に、CarrierWave でファイルアップロード機能を追加します。


Railsの環境はRails + Unicorn + Nginx - われプログラミングする、ゆえにバグありを参照してください。


Scaffold で Model と CRUD を作成する
$ bundle exec rails g scaffold photo image:string description:text

db:migrate を実行しておきます

$ bundle exec rake db:migrate

"photo" model の "image" フィールドで画像ファイルを扱います。
ここではまだ "image" は String のフィールドです。 

 
Webブラウザで "http://[host_name]/photos" を開いて、photo Model の CRUD 機能を確認しておきましょう。

CarrierWave の gem を追加

$ vim Gemfile

# 最終行に追加
gem 'carrierwave'

bundle install を実行しておきます

$ bundle install

 

Uploaderクラスを生成

$ bundle exec rails g uploader image
 # -> "app/uploaders/image_uploader.rb" というファイルが生成されます

 

Model を編集

$ vim app/model/photo.rb

  class Photo < ActiveRecord::Base
+   mount_uploader :image, ImageUploader
  end

 

View を編集

$ vim app/views/photos/_form.html.erb

# 1行目あたり
- <%= form_for(@photo) do |f| %>
+ <%= form_for(@photo, html: {multipart: true}) do |f| %>

# 16行目あたり
- <%= f.text_field :iamge %>
+ <%= f.file_field :iamge %>

$ vim app/views/photos/show.html.erb

# 5行目あたり
- <%= @photo.image %>
+ <%= image_tag @photo.image.url %>


以上で単純なファイルアップロード機能ができました。
 

その他の設定

アップロードされるファイルを画像に限定する

ファイル名の拡張子をチェックするだけです

$ vim app/uploaders/image_uploader.rb

# 41行目あたりをアンコメント
- #def extension_white_list
- #  %w(jpg jpeg gif png)
- #end
+ def extension_white_list
+   %w(jpg jpeg gif png)
+ end

 

アップロードしたファイルを削除できるようにする

$ vim app/views/photos/_form.html.erb

# 18行目あたり
  </div>
+ <div class="field">
+   <%= f.label :remove_image %>
+   <%= f.check_box :remove_iamge %>
+ </div>
  <div class="field">

$ vim app/controllers/photos_controller.rb

# 72行目あたり
- params.require(:photo).permit(:image, :description)
+ params.require(:photo).permit(:image, :remove_image, :description)

 

登録時のファイルをキャッシュする

登録時にValidationに引っ掛かって登録画面を再表示したときに、ファイルを再度選択しなくてもよくする

$ vim app/views/photos/_form.html.erb

# 17行目あたり
  <%= f.file_field :image %>
+ <%= f.hidden_field :image_cache %>

$ vim app/controllers/photos_controller.rb

# 72行目あたり
- params.require(:photo).permit(:image, :remove_image, :description)
+ params.require(:photo).permit(:image, :image_cache, :remove_image, :description)

 

登録の際にユニークなファイル名を付ける

クライアントがファイルをキャッシュする対策

$ vim app/uploaders/image_uploader.rb

参考 CarrierWaveで一意な名前をつけて画像を保存 | デジタネイティブ

# 47行目あたり
- # def filename
- #   "something.jpg" if original_filename
- # end
+ def filename
+   "#{secure_token}.#{file_extension}" if original_filename.present?
+ end
+
+ protected
+ def secure_token
+   var = :"@#{mounted_as}_secure_token"
+   model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
+ end

※ 新規登録の際、filename メソッドは model の id が確定していない状態で呼ばれるので、model.id は filename で使えない

 

まとめ

CarrierWave の機能として、他にリサイズ、サムネイルの作成等があります。