Rails、carrierwaveの使い方。インストール方法

## はじめに
CarrierWaveの備忘録。
Railsは5。

## Brew
ImageMagickをあらかじめ入れておく。
画像をS3に置くだけなら要らないけど、たいていの場合は加工するはず。

```
brew install imagemagick
```

## Gemfile
RailsからS3にアクセスするためのGemと画像加工用にRMgickを入れておく。

```
# 画像保存
gem 'carrierwave'
# For Carrierwave(S3用)
gem 'fog'
# 画像加工用
gem 'rmagick'
```

## 設定ファイル
S3の設定を記載しておく。

```ruby:initializers/carrierwave.rb
# S3を使用しているかを判定するためのメソッド。（ローカル環境ではS3を使わないため）
def use_s3?
  ENV['S3_ACCESS_KEY'] && ENV['S3_SECRET_KEY'] && ENV['S3_REGION'] && ENV['S3_BUCKET']
end

## CarrierWaveの設定
CarrierWave.configure do |config|
  # S3の設定
  if use_s3?
    config.fog_credentials = {
        :provider               => 'AWS',
        :aws_access_key_id      => ENV['S3_ACCESS_KEY'],
        :aws_secret_access_key  => ENV['S3_SECRET_KEY'],
        :region                 => ENV['S3_REGION'],
        # :host                   => '必要なら設定する'
        # :endpoint               => '必要なら設定する'
    }

    # S3のバケットを指定。
    config.fog_directory     = ENV['S3_BUCKET']
    # 一般公開させて無いS3の場合は以下の設定を行う。
    config.fog_public     = false
    # 一般公開されていない場合は以下の設定をする事で60秒間有効なURLを発行してくれる。
    config.fog_authenticated_url_expiration = 60
    CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
  end

  # public配下にキャッシュができると参照されてしまうので、予め変えておく。
  config.cache_dir = "#{Rails.root}/tmp/uploads"
end
```

## モデル
画像は`String`型で定義する。

```ruby:migrate.rb
class Images < ActiveRecord::Migration[5.0]
  def change
    create_table :images do |t|
      # 管理したい画像
      t.string :image
      # etc...

      t.timestamps
    end
  end
end
```

```ruby:model.rb
# 画像をアップロードする際のルール（別ファイルにしておけば汎用的に使える。）
class ImageUploader < CarrierWave::Uploader::Base
  # S3を使用するか？
  if use_s3?
    storage :fog
  else
    storage :file
  end

  # S3やローカルの保存先。（以下の書式をそのまま書いておけば大抵問題無い）
  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  # 以下のようなことができる。

  # リサイズしたり画像形式を変更するのに必要
  include CarrierWave::RMagick

  # 画像を100x100にリサイズする。
  process :resize_to_fill => [100, 100]

  # 保存形式をJPGにする
  process :convert => 'jpg'

  # サムネイルを生成する設定
  # version :thumb do
  #   process :resize_to_fill => [40, 40, gravity = ::Magick::CenterGravity]
  # end

  # jpg,jpeg,gif,pngしか受け付けない
  def extension_white_list
    %w(jpg jpeg png)
  end

  # 拡張子が同じでないとPNGをJPGとかにコンバートできないので、ファイル名を変更
  def filename
    super.chomp(File.extname(super)) + '.jpg' if original_filename.present?
  end

  # 別のバケットにアクセスする場合は以下のようにする。
  # https://blog.hello-world.jp.net/ruby/1449/
  # def fog_directory
  #   config = YAML.load_file("#{Rails.root}/config/carrierwave.yml")[Rails.env]
  #   config['fog_another_directory']
  # end

end

# 画像用モデル
class Image < ApplicationRecord
  # ファイルアップロード処理
  mount_uploader :image, ImageUploader
end
```

## View
特に大した事はせず。

```haml
= simple_form_for(@image) do |f|
  -#アップロードする画像（確認画面を作ることもできるけど、本記事では割愛）
  = f.input :image, as: :file
  -#アップロードした画像を表示
  = image_tag f.image
  -#カラム名にremoveをつけてデータを送ると画像を消せる。
  = f.input :remove_image, as: :boolean
  = f.submit "画像アップ"
```

## Controller
StrongParamsなどいろいろ割愛。。。

```ruby
def post_image
  Image.new(params[:image]).save
end
```

## まとめ
今まで自分でこれらのことをしてたけど、`CarrierWave`を使うととても楽だった。
ただし、`open`などでデータを取得して画像を設定したい場合にはややイレギュラーなことをする必要があった。
また、DBにblobとして格納する場合は`CarrierWave`は使えない。
なので、要件に合わせて頑張る必要がある。

### おまけ
URLから画像を拾ってきて画像を保存する方法。
もっといい方法があったら知りたい。

```ruby
open(path, :allow_redirections => :safe) do |file|
  # 一時的にテンポラリーファイルを作る必要があった。
  temp_img_file = Tempfile.new('ファイル名')
  temp_img_file.binmode
  temp_img_file << file.read
  temp_img_file.rewind
  # UploadedFileもしくはCarrierWave::Uploader::Base形式にする必要がある。
  uploaded_file = ActionDispatch::Http::UploadedFile.new(
    filename: 'ファイル名',
    type: file.content_type,
    tempfile: temp_img_file)
  image.image = uploaded_file
  image.save
end
```

#### 画像が横になる場合
以下のコードを記載する。

```ruby:ImageUploader
  process :fix_rotate

  # 画像の向きを調整する。
  def fix_rotate
    manipulate! do |img|
      img = img.auto_orient
      img = yield(img) if block_given?
      img
    end
  end
```