投稿機能でS3に画像をアップロード機能(アップロード)

##画像のアップロード先をS3に変更
CarrierWaveによる画像のアップロード先がアプリ内のpublicフォルダでしたが、これをS3に変更します。
以下の手順で変更していきます。
1.インターネット上にファイルをアップロードするためのGem（fog-aws）をインストールする
2.アップロードにfogを使うよう設定する
3.fogのアップロード先の情報を設定する
4.AWSのキーを安全に扱えるようにする
###fog-awsをインストール
fogとは画像をアップロードする際、外部のストレージを選択しアップロードするのを補助してくれるGemです。
Gemfile

```
〜省略〜

group :development do
  # Access an IRB console on exception pages or by using <%= console %> in views
  gem 'web-console', '~> 2.0'

  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
end

gem 'carrierwave'
gem 'fog-aws'  #追加
```
開発・テスト・本番環境いずれでも使えるようにするため、group ~ end で囲まれていない部分に追記します。
ターミナル

```
bundle install
```
###アップロードにfogを使用するための設定
今はアップロード先がpublicフォルダになっているので、これをfogを使う設定をするため、image_uploader.rbを編集します。

```app/uploaders/image_uploader.rb
# encoding: utf-8

class ImageUploader < CarrierWave::Uploader::Base

  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick
  process resize_to_fit: [800, 800]

  # Choose what kind of storage to use for this uploader:
  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
〜省略〜
```
storageを「:file」から「:fog」に変更することで、アップロードにfogを使う指定をします.
###fogのアップロード先の設定
次に、fogが使用するアップロード先の情報を設定します。
そのため、CarrierWaveの設定ファイルを新規作成します。
アプリケーションのルートからconfig/initializers直下に、carrierwave.rbというファイルを作成してください。

```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_provider = 'fog/aws'
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: Rails.application.secrets.aws_access_key_id,
    aws_secret_access_key: Rails.application.secrets.aws_secret_access_key,
    region: 'ap-northeast-1'
  }

  config.fog_directory  = 'ここにバケット名を入れます'
  config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/ここにバケット名を入れます'
end
```
バケット名の部分は、自身で付けられた名前を入力してください。
regionで設定してある「ap-northeast-1」は、アジアパシフィック（東京）を表しています。
もしも他のリージョンを使用する場合は、インターネットで検索してください。
config.asset_hostは「https://s3-ap-northeast-1.amazonaws.com/sample-name」のような記述になっているることを確認してください。
###安全にAWSのキーを扱えるように設定
次に、S3で使用するキーの設定を行います。ちゃんと設定しないと、万が一漏洩すると巨額の被害を被る恐れがありますので、しっかり確認してから進めていきましょう。
アプリ名でAWSのキーを扱う際は、以下の3箇所で設定を行うことで安全性を確保します。
CarrierWave=>CarrierWaveの設定ファイル
secrets.yml=>Railsアプリケーション全体の秘密情報を管理するファイル
環境変数=>でOSが提供するデータ共有の仕組み漏洩のリスクが低い
AWSのidやパスワードそのものは「環境変数」だけに設定します。
環境変数に代入されているデータは誤操作によって漏れてしまうことがないため、安全性が高いです。
そして、環境変数の値を「secrets.yml」から読み込んで、さらにその内容を「carrierwave.rb」が読み込むという構造になっています。
環境変数の設定はS3の接続に必要な認証情報を、環境変数として設定します。
IAMユーザー設定時にダウンロードしたCSVファイル（２つ目のダウンロードファイル）を開き、その中に、Access_key_IDとSecret_access_keyというカラムがあるので、こちらに書かれた値を設定します。
この作業においては、コマンドは基本的にどこのディレクトリで入力していただいても問題ありません。特に理由がなければホームディレクトリまたはルートディレクトリで実行します。
環境変数の設定の仕方は、変数の一種なのでパソコンをシャットダウンすると消えてしまいます。それだと毎回設定しなくてはいけないので、シェルの起動時に自動で読み込まれるファイルに設定内容を記述します。
環境変数の設定方法はmacOSのバージョンによって異なります。そのため、以下の手順で確認を行います。
デスクトップの左上にあるアイコンをクリックして、「このMacについて」を選択します。
macOSのバージョンを確認することができます。
###MacOSがCatalina(10.15以降)の場合
ターミナル

```
# ローカル環境
$ vim ~/.zshrc

# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
export AWS_ACCESS_KEY_ID='ここにCSVファイルのAccess key IDの値をコピー'
export AWS_SECRET_ACCESS_KEY='ここにCSVファイルのにSecret access keyの値をコピー'

# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了
```
###MacOSがMojave以前(10.14以前)の場合
ターミナル

```
# ローカル環境
$ vim ~/.bash_profile

# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
export AWS_ACCESS_KEY_ID='ここにCSVファイルのAccess key IDの値をコピー'
export AWS_SECRET_ACCESS_KEY='ここにCSVファイルのにSecret access keyの値をコピー'

# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了
```
※上記の設定は、現在の設定に「追加」してください。既存の設定を削除してしまうとパソコンが正常に動作しなくなる危険性があります。
次は、以下のコマンドを実行して設定を反映させます。
ターミナル

```
 ローカル環境
# 編集した.zshrcを読み込み直して、追加した環境変数を使えるようにする
$ source ~/.zshrc
```
これで環境変数が設定できました。
###secrets.ymlの設定
**※ここからはOSに関係なく、Mojave以前の方もCatalinaの方も同じ作業です。**
次にsecrets.ymlの設定を行い、環境変数の値を読み込みます。
YAMLファイル
拡張子がymlのファイルは、YAMLと呼ばれる種類のファイルです。
設定ファイルとしてよく使われ、Rubyのハッシュのように「：（コロン）」で区切って「キー」「バリュー」を設定することができます。
secrets.ymlもYAMLファイルです。

```config/secrets.yml
# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: cb2965bfebd75267542611a74ab612b9754f98・・・・・
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>   #追加
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>   #追加

test:
  secret_key_base: 7362cb8e960adf75f110e17bb4cd1f2d4edc3d・・・・・

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
```
aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>という設定を追加しています。
右側のENV[" "]という書き方は、環境変数を読み込むためのRubyのメソッドです。かっこの中には環境変数名を指定します。
つまりaws_access_key_idというキーに対して、環境変数AWS_ACCESS_KEY_IDの値をセットするという意味になります。
secrets.ymlには直接パスワードなどを記載しません。そのため、万が一このファイルが漏洩しても第三者にはパスワードはわかりません。
ここで、carrierwave.rbの設定を確認してみます。
secrets.ymlの設定まで終わりましたので、続いてCarrierWaveからこの設定を読み込めるようにする必要があります。

```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_provider = 'fog/aws'
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: Rails.application.secrets.aws_access_key_id,
    aws_secret_access_key: Rails.application.secrets.aws_secret_access_key,
    region: 'ap-northeast-1'
  }

  config.fog_directory  = 'ここにバケット名を入れます'
  config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/ここにバケット名を入れます'
end
```
値に「Rails.application.secrets.aws_access_key_id」という記述があります。
この前半の「Rails.application.secrets」という書き方によって、secrets.ymlの設定内容を呼び出すことができます。
続けて「aws_access_key_id」と書いてあるので、secrets.ymlで設定した「aws_access_key_id」の値という意味になります。
ここまでの設定を行うことで、「環境変数」→「secrets.yml」→「carrierwave.rb」の順にデータを読み込めたことになります。
###.gitignoreを編集
他人に知られてはいけない内容については、Gitの管理から外してアップロードされないようにします。
「.gitignore」というファイルに設定を行うことで、Gitの管理下から除外することができます。
ここまでの設定によって、secrets.ymlにはパスワードの記述はありません。そのため、今の段階でアップロードしても問題はありませんが、secrets.ymlは各種パスワードを扱うためのファイルなので、必ず以下の操作を行って、pushされないようにします。
.gitignore

```
# ファイルの最下部に下記を追記
config/secrets.yml
```
※.gitignoreに記載した変更は、一度gitの監視下に置かれてしまったファイルには適用されません。変更を反映させるには、アプリ名のディレクトリでコマンドを実行して、config/secrets.ymlをgitの監視から外します。
ターミナル

```
$ cd アプリ名
$ git rm --cached config/secrets.yml
```
###アップロードしたファイルがS3に保存されるかの確認
ローカル開発環境のアプリから、画像付きの投稿を行います。
次に、AWS内のS3のバケットを見て、ファイルがアップロードされているか確認します。
正常に投稿できていれば、バケットを選択した際に「uploads」というディレクトリができています。
さらに下の階層にたどっていくと、アップロードした画像が保存されていることが確認できます。
##本番環境からS3にアップロード
今まで変更したきた内容でデプロイをし直して、本番環境でもS3に画像がアップロードされるようにします。
ですが、このままだとデプロイし直しても環境変数が原因で動作しません。
環境変数とはOSが提供する変数です。ローカル環境と本番環境とでは動作している端末が別なので、環境変数も全くの別物です。
そのため、本番環境用の環境変数を別途設定します。
###本番環境の環境変数の設定
本番環境の設定変更をするので、sshでリモートのサーバーにログインしてから作業します。
ターミナル(ec2-user)

```
# 本番環境
$ ssh -i [pem鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
（ダウンロードした鍵を用いて、ec2-userとしてログイン）
$ sudo vim /etc/environment
# iを押してインサートモードに移行し、下記を追記する。既存の記述は消去しない。
AWS_ACCESS_KEY_ID='ここにCSVファイルのAccess key IDの値をコピー'
AWS_SECRET_ACCESS_KEY='ここにCSVファイルのにSecret access keyの値をコピー'
# 編集が終わったらescapeキーを押してから:wqと入力して保存して終了
```
次は、環境変数の設定を反映させます。
ターミナル(ec2-user)

```
# 本番環境
# 編集した環境変数を適用するために一旦ログアウトします。
$ exit
$ ssh -i [pem鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
# 環境変数が適用されているか確認しましょう。
$ env | grep AWS_SECRET_ACCESS_KEY
$ env | grep AWS_ACCESS_KEY_ID
```
なぜ、本番環境での環境変数を「environment」というファイルに書き込んだかについて
ローカルでは「.bash_profile」に記述することで、環境変数の設定を行いましたが、本番環境ではそれでは環境変数の設定ができません。
通常は、ユーザーがログインを行うと「.bash_profile」が読み込まれ、その内容が実行されます。
しかし、Capstranoを使用した場合はその読み込みが行われないため、別の方法で環境変数を設定する必要があります。それが「/etc/environments」に記述する、という方法になります。
###本番環境での環境変数読み込みの設定
ローカルは開発環境用のsecrets.ymlの設定を行いましたが、今回は本番環境用の設定を行います。

```config/secrets.yml
# Be sure to restart your server when you modify this file.

# Your secret key is used for verifying the integrity of signed cookies.
# If you change this key, all old signed cookies will become invalid!

# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
# You can use `rails secret` to generate a secure secret key.

# Make sure the secrets in this file are kept private
# if you're sharing your code publicly.

development:
  secret_key_base: cb2965bfebd75267542611a74ab612b9754f98・・・・・
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>

test:
  secret_key_base: 7362cb8e960adf75f110e17bb4cd1f2d4edc3d・・・・・

# Do not keep production secrets in the repository,
# instead read values from the environment.
production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
  aws_access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>   #追加
  aws_secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>   #追加
```
上記で、secrets.ymlの設定を変更しましたが、このファイルはGitで管理されないようにしています。
そのため、Capstranoで自動デプロイを行っても本番環境には送られません。
この問題を解決するために、Capistranoの設定を変更します。

```config/deploy.rb
# secrets.yml用のシンボリックリンクを追加
set :linked_files, %w{ config/secrets.yml }

# 元々記述されていた after 「'deploy:publishing', 'deploy:restart'」以下を削除して、次のように書き換え

after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
  task :restart do
    invoke 'unicorn:restart'
  end

  desc 'upload secrets.yml'
  task :upload do
    on roles(:app) do |host|
      if test "[ ! -d #{shared_path}/config ]"
        execute "mkdir -p #{shared_path}/config"
      end
      upload!('config/secrets.yml', "#{shared_path}/config/secrets.yml")
    end
  end
  before :starting, 'deploy:upload'
  after :finishing, 'deploy:cleanup'
end
```
set :linked_filesで指定されたファイルは、元々のディレクトリを参照する代わりに、shared/元々のディレクトリ構成を参照するようになります。今回の例で言うと、config/secrets.ymlを参照する代わりに、shared/config/secrets.ymlを参照するようになります。
desc upload secrets.yml以下の記述は、ローカル環境にあるconfig/secrets.ymlを本番環境のshared/config/secrets.ymlに反映するための設定を行なっています。これで、.gitignoreに記載されているsecrets.ymlを、Githubを経由せずにデプロイすることができます。
次は、ここまでの作業を本番環境に反映させます。
変更内容をコミットしてGithubにプッシュ（publish）します。
次に、自動デプロイを実行します。
ターミナル（ローカル）

```
$ bundle exec cap production deploy
```
本番環境に反映の仕方は
①GitHubへのpush
②「bundle exec cap production deploy」の実行
です。
###正しくアップロードできるかの確認
まず、本番環境のアプリから、画像つきの投稿をしてください。
次に、最初に今の時点でS3にいくつのファイルがアップロードされているか確認してください。
次に、「本番環境」のから画像投稿を行い、もう一度S3のファイルを確認します。
今投稿したファイルがS3上で確認できれば成功です。
動作確認ができたらプルリクエストを作成します。