【CarrierWave】EC2上でS3に画像を保存する方法【MiniMagick】

# 目標
![tweettech1.gif](https://qiita-image-store.s3.amazonaws.com/0/178077/0d83aad1-5c50-70c8-27db-b82e872cd3b4.gif)

* AWSのEC2インスタンス上で画像投稿を行い、ファイルがS3に投稿されるようにする。
* EC2インスタンスや基本的な機能は作成済みのものとします。

# 手順
* CarrierWaveを使用して画像を保存・表示できるように設定する
* MiniMagickを使用して画像サイズを変えられるように設定する。
* fogを使用して外部のストレージを使用できるよう設定する
* AWSのS3に画像が保存されるように設定する
* EC2インスタンス上で動作するよう設定する
* デプロイする

# CarrierWaveを使用して画像を保存・表示できるように設定する
* CarrierWaveをインストールします。

````ruby:Gemfile
gem 'carrierwave'
````
* ターミナルで`bundle install`を実行します。
* `rails s`でサーバーを再起動します。忘れると**uninitialized constant**というエラーが出てしまうので気をつけましょう。

* ターミナルで`rails g uploader 画像を保存するカラム名`を実行し、`(カラム名)_uploader.rb`を作成します。

````:ターミナル
rails g uploader image
````

* 同時に、appディレクトリの中にuploadersディデクトリができます。
* モデルで`mount_uploader`を記述し、uploader.rbを読み込む設定をします。

````ruby:tweet.rb
class Tweet < ApplicationRecord
  belongs_to :user

  # mount_uploader :カラム名, uploader名（大文字でつなげる）
  mount_uploader :image, ImageUploader
end

````

* 下記のように画像を保存するフォームを用意します。`form_for`の場合だと`f.file_field :カラム名`となります。

````erb:tweet.html.erb
<%= form_for(@item) do |f| %>
  <div class="field">
    <%= f.text_area :content %>
    <%= f.file_field :image %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
````

* コントローラーでストロングパラメーターを設定している場合は忘れずにカラム名を追記しましょう。

````ruby:tweets_controller
def tweet_params
  params.require(:tweet).permit(:content, :image)
end
````

* deviseのカラムに保存する場合はapplication_controllerでストロングパラメーターを設定します。
* デフォルトにないカラム名を`keys`に記述します。（例では`icon`と`wallpaper`）

````ruby:application_controller
 before_action :configure_permitted_parameters, if: :devise_controller?

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:icon, :wallpaper])
    devise_parameter_sanitizer.permit(:account_update, keys: [:icon, :wallpaper])
  end
````

* 画像を表示するには`image_tag`を使用します。カラム名に`.url`をつけるのを忘れずに。
 
````erb:views/tweets/index
<%= image_tag @tweet.image.url %>
````

* これでローカル環境で画像をアップロードできます。

# MiniMagickを使用して画像サイズを変えられるように設定する
* 画像加工処理のgemとして**RMagick**が有名ですが、エラーが多発するため、ほぼ同じことができる**MiniMagick**の使用をオススメします。

````Gemfile
gem 'mini_magick'
````

* `bundle install`とサーバーの再起動を忘れずに。

* MiniMagickの使用にはImageMagickをインストールする必要があります。
* `which convert`でパスが表示されればインストールされています
* インストールされていない場合は`brew install imagemagick`でインストールします

````terminal:ターミナル
MikanBook-Pro:tweet-tech mikan3rd$ which convert
/usr/local/bin/convert
````

* uploader.rbを編集します。`include CarrierWave::MiniMagick`と記述しましょう。
* 画像サイズを変えるには`resize_to_fit`や`resize_to_limit`を使います。[横サイズ, 縦サイズ]と指定します。
* `extension_white_list`を定義することで拡張子を指定できます。

````ruby:image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base

  include CarrierWave::MiniMagick

  strage :file

  process resize_to_limit: [500, 500]

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

end
````

* 他にも別で画像を保存するカラムがある場合は`rails g uploader 画像を保存するカラム名`で新たにuploaderファイルを作成し、同様の方法で設定します。
* 同じ画像を違うサイズで保存したい場合、下記のようにversionを指定します。
* この場合、`<%= image_tag @tweet.image.garalley.url %>``<%= image_tag @tweet.image.thumb.url %>`のように、カラム名の下にversion名を加えることで、そのversionの形式の画像を表示できます。

````ruby:image_uploader
class ImageUploader < CarrierWave::Uploader::Base

  include CarrierWave::MiniMagick

  version :garalley do
    process resize_to_fit: [200, 200]
  end

  version :thumb do
    process resize_to_fit: [100, 100]
  end

end

````

* 単純に、CSSのwidth/heightで調整することもできます。

# fogを使用して外部のストレージを使用できるよう設定する
* fogをインストールします。fogは画像をアップロードする際、外部のストレージを選択してアップロードするのを補助してくれるgemです。

````ruby:Gemfile
gem 'fog'
````

* bundle installとサーバーの再起動を忘れずに。
* uploader.rbでstrageをfogにします。

````ruby:image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base

  include CarrierWave::MiniMagick

  process resize_to_limit: [550, 500]

  # storage :file から :fog に変更
  storage :fog

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

end
````

* これでfogを使う準備ができました

# AWSのS3に画像が保存されるように設定する
* AWSの「サービス」で「S3」を選択し、バケットを作成しましょう。
* 日本の場合、リージョンは「アジアンパシフィック（東京）」にしましょう。

* config/initializersディレクトリにcarrierwave.rbを作成します。

````ruby:config/initializers/carrierwave.rb
require 'carrierwave/storage/abstract'
require 'carrierwave/storage/file'
require 'carrierwave/storage/fog'

CarrierWave.configure do |config|
  config.storage = :fog
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: ENV['AWS_ACCESS_KEY_ID'],
    aws_secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'],
    region: ENV['AWS_REGION']
  }

    case Rails.env
    when 'development'
        config.fog_directory  = ENV['AWS_BUCKET_NAME']
        config.asset_host = ENV['AWS_BUCKET_URL']
    when 'production'
        config.fog_directory  = ENV['AWS_BUCKET_NAME']
        config.asset_host = ENV['AWS_BUCKET_URL']
    end
end
````

* まずはローカル環境で動作するように、環境変数を.bash_profileに記述します。
* 環境変数とはサーバに登録して参照できる変数で、外部に漏らしたくないパスワードなどの情報を設定します。

````:ターミナル
MikanBook-Pro:tweet-tech mikan3rd$ cd
MikanBook-Pro:~ mikan3rd$ vi .bash_profile
````

````ruby:.bash_profile
export AWS_ACCESS_KEY_ID=*******************
export AWS_SECRET_ACCESS_KEY=************************
export AWS_REGION=ap-northeast-1
export AWS_BUCKET_NAME=mikan3rd-tweet-tech
export AWS_BUCKET_URL=https://s3-ap-northeast-1.amazonaws.com/mikan3rd-tweet-tech
````

* バケットのリージョンが東京の場合、`export AWS_REGION=ap-northeast-1`となります。他の項目も例にならって設定しましょう。

* Railsのディレクトリに戻ってから`source ~/.bash_profile`で.bash_profileを再読み込みします。
* サーバーも再起動します

````:ターミナル
MikanBook-Pro:~ mikan3rd$ cd projects/tweet-tech/
MikanBook-Pro:tweet-tech mikan3rd$ source ~/.bash_profile
MikanBook-Pro:tweet-tech mikan3rd$ rails s
````

* 以上で画像の保存先をS3に設定できました

# EC2インスタンス上で動作するよう設定する
* デプロイ前に、EC2で環境変数の設定とImageMagickのインストールを行う必要があります。
* EC2にログイン後、`sudo vim /etc/environment`で環境変数を設定します。
* ImageMagickのインストール方法はいくつかあるようですが、私は下記の方法でインストールできました。
* https://gist.github.com/ARolek/9199329

# デプロイする
* 以上です

内容に不備等ありましたら、お手数ですがコメントにてお願いします。