HerokuでS3に画像をアップロードした話[Rails][S3][CarrierWave][fog]

転職活動用にポートフォリオを作っています。
DTPの作品も多く、画像が全部でおよそ18MBあったのですが、Herokuの無料版の容量は５MBのため、画像の外部ストレージとしてS3を利用するようにしました。

調べたら、多くの人がつまづいているようでしたので、自分のためにも手順をメモしておこうと思います。

# S3の利用設定
まずは、S3の利用設定を行います。手順については、こちらのサイトを大変参考にさせていただきました。

[【Rails】S3へ『CarrierWave+fog』を使って画像アップロードする方法][1]
[1]:http://vdeep.net/rubyonrails-carrierwave-s3

↑こちらの記事以上に丁寧な説明はないと思いますので、S3の設定についてはこちらをご参照いただければ幸いです。

# gemをインストール
画像アップロードに必要な、以下のgemをインストールします。

- `carrierwave`…画像をアップロードするgem
- `mini_magick`…画像をリサイズするgem
- `fog`…S3などのクラウドストレージ内の画像を参照するgem

それぞれ、設定の書き方は以下の通り。

```ruby:Gemfile
group :production do
  gem 'pg',             '0.18.0'
  gem 'rails_12factor', '0.0.2'
  gem 'fog-aws'
end

gem 'carrierwave'
gem 'mini_magick'
```

忘れずに`bundle install`します。

なお、私の場合ですが`fog`というgemではaws関連のdependencyのgemがインストールされず、`fog-aws`を利用しないと先に進めませんでした。

# アップローダーの生成
　CarrierWaveをインストールすると、画像のアップローダーの生成ができるようになりますので、以下のコマンドで生成します。

```
$ rails generate uploader カラム名
```
最後の「カラム名」は、画像を必要とするモデルのデータベースのカラム名です。**単数形、頭文字大文字**で表記します。私は、すでにあったテーブルのカラム名から、`Image`にしました。

なお、画像のファイル名が文字列のため、imageカラムのデータ型はstring型です。

```
$ rails generate uploader Image
      create  app/uploaders/image_uploader.rb ←生成
```
CarrierWaveに、imageカラムがuploaderと関連していることを認識させるためには、`mount_uploader`というメソッドを使います。（今回モデル名は`work`）

```ruby:models/work.rb
class Work < ApplicationRecord
  mount_uploader :image, ImageUploader
end
```
#　viewファイルの書き換え
そして、今度はビューファイルをいくつか変更します。まずは、ファイルをアップロードする機能をviewに持たせます。画像をアップロードするフォームのタグを`file_field`にしました。

```haml:views/works/new.html.haml
= f.label :image, "画像："
= f.file_field :image
```
そして、workの一覧ページの書き方も、CarrierWaveに合わせて書き換えます。

```haml:views/index.html
-# 書き換え前
- @works.each do |work|
   = image_tag work.image

-# 書き換え後
- @works.each do |work|
  = image_tag work.image.url
```
**work.imageの後にurlをつける**のがポイントです。
以上の動作により、無事画像のアップロードができました。

[![Image from Gyazo](https://i.gyazo.com/3e9be6710d2ea32467a62f0545ff39a3.jpg)](https://gyazo.com/3e9be6710d2ea32467a62f0545ff39a3)

# 本番環境で画像のアップロード先を変える

この時、画像ファイルはデフォルトでは、ローカルのフォルダにアップロードさせています。

```ruby:views/uploaders/image_uploader.rb
storage :file
```
`storage :file`という記述は、ローカルのファイルシステムに画像を保存するための記述です。

画像で示すと、こちらにありました。
[![Image from Gyazo](https://i.gyazo.com/2ec74d2e38292b8e26d1c8b44f35b910.png)](https://gyazo.com/2ec74d2e38292b8e26d1c8b44f35b910)

これだと、Herokuにデプロイした時に容量を圧迫してしまうので、本番環境だけS3に画像をアップするようにしたいと思います。

```ruby:views/uploaders/image_uploader
if Rails.env.production?
  storage :fog
else
  storage :file
end
```

`if Rails.env.production?`で本番環境ではクラウドストレージに保存するようにしています。
さらに、同じく、image_uploaderに下記のように記載します。

```ruby:views/uploaders/image_uploader
if Rails.env.production?
    CarrierWave.configure do |config|
      config.fog_credentials = {
        # Amazon S3用の設定
        :provider              => 'AWS',
        :region                => ENV['S3_REGION'],  # S3に設定したリージョン。
        :aws_access_key_id     => ENV['S3_ACCESS_KEY'],
        :aws_secret_access_key => ENV['S3_SECRET_KEY']
      }
      config.fog_directory     =  ENV['S3_BUCKET']
    end
```
それぞれ、以下のように設定します。ちなみに、東京のリージョンは`ap-northeast-1`です。

```
$ heroku config:set S3_ACCESS_KEY="S3のAccessキーを入力"
$ heroku config:set S3_SECRET_KEY="同様に、Secretキーを入力"
$ heroku config:set S3_BUCKET="Bucketの名前を入力"
$ heroku config:set S3_REGION="Regionの名前を入力"
```
ここまで書いたらようやく本番環境にデプロイです。デプロイして動作を試します。

・・・できた！！！無事アップロード完了です！！

[![Image from Gyazo](https://i.gyazo.com/d99efafbfd7a3a9d1cbed4d1a2c5b951.png)](https://gyazo.com/d99efafbfd7a3a9d1cbed4d1a2c5b951)

## 追伸：エラーへの対処
なお、本番環境ではすぐに順調に動作をしたわけではなく、私の場合は`Access Denied`というエラーに遭いました。

S3の**パブリックアクセス設定を変更する**を変更したところ、なんとかなりましたので、ここに情報を共有しておきます。
[![Image from Gyazo](https://i.gyazo.com/512ffef431c41960f07ebfbf6f651552.png)](https://gyazo.com/512ffef431c41960f07ebfbf6f651552)