rails+heroku+aws s3の構成で画像を保存する

# やりたいこと

画像を投稿できるようにしたいがherokuだと一時的な保存になるということなのでawsのs3に保存する。

# 全体構成

調べてみると２つ実装方法があった。今回はcarrierwaveを選択。

## paperclip

paperclip
https://github.com/thoughtbot/paperclip

## carrierwave

carrierwave
https://github.com/carrierwaveuploader/carrierwave

# 全体の設計

* carrierwaveを使い、fileuploaderの作成
* fogでawsのs3に繋ぎ、s3に画像を保存
* URLを画像に付与してそのURLを元に表示

# awsアカウント作成

https://qiita.com/junara/items/1899f23c091bcee3b058#aws

この記事のアカウント作成部分を参考に。バケットの状態については下記

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/user-guide/bucket-permissions-check.html

* Public – 世界中の誰でも、または認証された AWS ユーザーがパブリックアクセス可能。
* Not public* – バケットはパブリックアクセス可能ではありません。ただし、バケットのオブジェクトは、オブジェクト ACL によりパブリックアクセス可能の場合があります。
* Access denied – バケットからロックアウトされています。
* Error – サービス関連のエラーが発生しました。
* Undetermined – Amazon S3 はバケットがパブリックアクセス可能かどうかを判断できません。

# fogとcarrierwaveのinstall

```ruby
gem 'carrierwave'
gem 'fog-aws'
```

ここがfogのまんまだと下記の記事のようなエラーが出る

https://halkyo.com/?p=868

# 画像保存用のモデル作成

## 新規のモデルに追加する場合

```ruby
$ rails g model image
```

```ruby
class CreatePhotos < ActiveRecord::Migration
  def change
    create_table :photos do |t|
      t.string :image
      t.timestamps
    end
  end
end
```

## 既存のモデルに追加する場合

```
$ rails g migration addcolumnimage
```

```ruby
class AddColumnimage < ActiveRecord::Migration[5.1]
  def change
    add_column :image, :string
  end
end
```

# carrierwaveの設定

## uploaderクラスの追加

```
rails generate uploader Image
```

```ruby:app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  if Rails.env.development?
    storage :fog
  elsif Rails.env.test?
    storage :fog
  else
    storage :fog
  end

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

  def extension_whitelist
    %w(jpg jpeg gif png)
  end
  # ここでファイル形式を指定する
  def filename
    original_filename if original_filename
  end
end

```

storage・・・storageでは画像の保存先を設定している。ここをlocalの開発環境でもawsに保存したい場合はfogにする。上記では全部fogだから分岐必要ないが、環境ごとに変えるなら変更する必要あり。
store_dir・・・fileのupload先を変更したいときに設定する。


## initializerの設定

下記のファイルを新規作成する

```ruby:config/initializers/carrierwave.rb

CarrierWave.configure do |config|
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: '',
    aws_secret_access_key: '',
    region: 'ap-northeast-1'
  }

  config.fog_directory  = 'rails-photo-123'
  config.asset_host = "https://s3.ap-northeast-1.amazonaws.com/バケット名"
  config.fog_public = false
  config.cache_storage = :fog
end
```

aws_access_key_id・・・awsのiamのaccesskey
aws_secret_access_key・・・awsのiamのsecretaccesskey
config.fog_directory・・・バケット名
config.asset_host・・・https://s3.リージョン名.amazonaws.com/バケット名

>fog_publicを利用することで、URLへの直アクセスを制御できる。 デフォルトはtrueらしい。 これがtrueの場合は、アップロードしたオブジェクトのアクセス許可に、 全員ダウンロードができる権限が付与される。

http://ubw.hatenablog.com/entry/2016/07/23/212841

fog_publicについては上記。基本はs3のブロックパブリックアクセス (バケット設定)を全てonにしてパブリックで見れないようにしてこのfog_publicもfalseにすることでパブリックにアクセスできないようにする方がセキュリティ的には良い。

### region

各regionの表

https://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html#s3_region

## modelにmount_uploader

```ruby
class Post < ActiveRecord::Base
  mount_uploader :image, ImageUploader
end
```

引数に(カラムの属性名, 生成されたアップローダーのクラス名)をとります。

# view側の設定

## form_forでタグ設置

入力用のformタグはform_forで生成する

```ruby
<%= form_for @post, :url => {:action => 'create'} do |f| %>
<%= f.file_field :image %>
<% end %>
```
form_forで生成するとpostの下に配列が入る。なのでparamsで取ってくるときは下記のように[:post][:image]のような形になる。

```ruby
@post = Post.new(title:params[:title],sns_id:params[:sns_id],sns:params[:sns],image:params[:post][:image],user_id:params[:user_id],description:params[:post][:description],post_type: params[:post_type])
```

https://qiita.com/shunsuke227ono/items/7accec12eef6d89b0aa9

form_forについてはこの記事が詳しい。

## 保存した画像を表示させる

```
<%= image_tag photo.image.to_s %>
```