【Rails】AWS S3へ画像のアップロードが出来た♪

2018年4月23日

前回の記事で「CarrierWaveとrmagickで画像アップロード機能」を作りました。

https://pg-happy.jp/carrierwave-rmagic-uploader.html

これによって開発環境(AWS Cloud9)への画像アップロードは出来たのですが、ここからAWS S3へ画像をアップロードできるようにしたいと思います。

前回の続きです。

Amazon AWS S3を利用する

1.Amazon AWSのアカウントを作成したら
サービスをクリックして

2.ストレージのところにあるS3をクリック

3.バケットを作成する

4.設定
バケット名をローマ字で入力します。これ後で使います。「s3bucket」とか自由に。
リージョンは米国東部(オハイオ)にしました。herokuにアップするので米国が良いのかなと思って。

で、[作成]か[次へ]を連続してクリックします。

5.バケット作成完了

この画面が出るのでバケット(バケツの意?)が作成されたことがわかります。

AWS S3アクセスキー等を作成する

Amazonのこちらのヘルプを参考に進めました。

https://aws.amazon.com/jp/developers/access-keys/

1.IAMコンソールにアクセスします。

2.グループをクリックしてグループを作成していきます

3.新しいグループの作成をクリック

4.適当なグループ名を入力して[次へ]
sdk_demo とか s3_group とか好きな文字を入力します。

5.検索窓に「S3」と入力し出てきたポリシーリストから「AmazonS3FullAccess」を選択し[次のステップ]へ進みましょう。

6.確認して[グループの作成]をクリック

7.画面左側のユーザーから

8.ユーザーを追加をクリック

9.ユーザー作成画面で
ユーザー名を入力し、プログラムによるアクセスにチェックを入れて次へ進みます。

10.先ほど作成したグループにユーザーを追加してユーザーの作成を行います。

11.ユーザーの作成に成功します。
CSVをダウンロードしておきましょう。

AccessKey IDと、Secret access keyの作成ができました。

ここからRailsに取り掛かります。

Gemfileにfog-awsを追加

はじめは各種参考サイトをみてGemfileに「fog」を追加してやっていたのですが、

gem 'fog'

これで

bundle install

するとエラーが出ました。

Installing ovirt-engine-sdk 4.2.4 with native extensions
Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    current directory: /usr/local/rvm/gems/ruby-2.4.1/gems/ovirt-engine-sdk-4.2.4/ext/ovirtsdk4c
/usr/local/rvm/rubies/ruby-2.4.1/bin/ruby -r ./siteconf20180422-6443-r098i1.rb extconf.rb
checking for xml2-config... no
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/local/rvm/rubies/ruby-2.4.1/bin/$(RUBY_BASE_NAME)
        --with-libxml2-config
        --without-libxml2-config
        --with-pkg-config
        --without-pkg-config
extconf.rb:29:in <code>&lt;main&gt;': The &quot;libxml2&quot; package isn't available. (RuntimeError)

To see why this extension failed to compile, please check the mkmf.log which can be found here:

  /usr/local/rvm/gems/ruby-2.4.1/extensions/x86_64-linux/2.4.0/ovirt-engine-sdk-4.2.4/mkmf.log

extconf failed, exit code 1

Gem files will remain installed in /usr/local/rvm/gems/ruby-2.4.1/gems/ovirt-engine-sdk-4.2.4 for inspection.
Results logged to /usr/local/rvm/gems/ruby-2.4.1/extensions/x86_64-linux/2.4.0/ovirt-engine-sdk-4.2.4/gem_make.out

An error occurred while installing ovirt-engine-sdk (4.2.4), and Bundler cannot continue.
Make sure that </code>gem install ovirt-engine-sdk -v '4.2.4'<code> succeeds before bundling.

In Gemfile:
  fog was resolved to 2.0.0, which depends on
    fog-ovirt was resolved to 1.0.3, which depends on
      ovirt-engine-sdk

バンドルする前に gem install ovirt-engine-sdk -v ‘4.2.4’が成功していることを確認してください。と読み取れるのですが意味がわかりません。

どうやって解決したかというと

gem 'fog-aws'

とGemfileに追記しました。これでインストールが滞りなく進みました。

image_uploader.rbの修正

/uploaders/image_uploader.rbのファイルの6行目あたり。

  # Choose what kind of storage to use for this uploader:
  storage :file
  # storage :fog

この部分を↓こうしました。

  # Choose what kind of storage to use for this uploader:
  #storage :file
  storage :fog

config/initializers/carrierwave.rb 作成

carrierwave.rbというファイルを新たに作成し、中身を以下のように記述。

CarrierWave.configure do |config|
  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: ENV['AWS_S3_ACCESS_KEY_ID'],
    aws_secret_access_key: ENV['AWS_S3_SECRET_ACCESS_KEY'],
    region: 'us-east-1'
  }

    case Rails.env
    when 'development'
        config.fog_directory  = '自分のバケツ名'
        config.asset_host = 'https://s3.amazonaws.com/自分のバケツ名'
    when 'production'
        config.fog_directory  = '自分のバケツ名'
        config.asset_host = 'https://s3.amazonaws.com/自分のバケツ名'
    end
end

.envファイルに環境変数を追記。

AWS_S3_ACCESS_KEY_ID = '自分のアクセスキーID'
AWS_S3_SECRET_ACCESS_KEY = 'シークレットアクセスキーID'

ところがこれだと、Railsの起動時にエラーが出ました。(ヽ´ω)

rails s -b $IP -p $PORT
=&gt; Booting Puma
=&gt; Rails 5.0.6 application starting in development on http://127.0.0.1:8080
=&gt; Run <code>rails server -h</code> for more startup options
Exiting
/usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:293:in <code>require': cannot load such file -- fog (LoadError)
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:293:in </code>block in require'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:259:in <code>load_dependency'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:293:in </code>require'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/carrierwave-1.2.2/lib/carrierwave/uploader/configuration.rb:122:in <code>eager_load_fog'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/carrierwave-1.2.2/lib/carrierwave/uploader/configuration.rb:137:in </code>fog_credentials='
        from /home/ec2-user/environment/koluku/config/initializers/carrierwave.rb:2:in <code>block in &lt;top (required)&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/carrierwave-1.2.2/lib/carrierwave/uploader/configuration.rb:159:in </code>configure'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/carrierwave-1.2.2/lib/carrierwave.rb:14:in <code>configure'
        from /home/ec2-user/environment/koluku/config/initializers/carrierwave.rb:1:in </code>&lt;top (required)&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:287:in <code>load'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:287:in </code>block in load'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:259:in <code>load_dependency'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/dependencies.rb:287:in </code>load'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/engine.rb:648:in <code>block in load_config_initializer'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/activesupport-5.0.6/lib/active_support/notifications.rb:166:in </code>instrument'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/engine.rb:647:in <code>load_config_initializer'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/engine.rb:612:in </code>block (2 levels) in &lt;class:Engine&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/engine.rb:611:in <code>each'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/engine.rb:611:in </code>block in &lt;class:Engine&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/initializable.rb:30:in <code>instance_exec'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/initializable.rb:30:in </code>run'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/initializable.rb:55:in <code>block in run_initializers'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:228:in </code>block in tsort_each'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:350:in <code>block (2 levels) in each_strongly_connected_component'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:422:in </code>block (2 levels) in each_strongly_connected_component_from'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:431:in <code>each_strongly_connected_component_from'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:421:in </code>block in each_strongly_connected_component_from'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/initializable.rb:44:in <code>each'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/initializable.rb:44:in </code>tsort_each_child'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:415:in <code>call'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:415:in </code>each_strongly_connected_component_from'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:349:in <code>block in each_strongly_connected_component'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:347:in </code>each'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:347:in <code>call'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:347:in </code>each_strongly_connected_component'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:226:in <code>tsort_each'
        from /usr/local/rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/tsort.rb:205:in </code>tsort_each'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/initializable.rb:54:in <code>run_initializers'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/application.rb:352:in </code>initialize!'
        from /home/ec2-user/environment/koluku/config/environment.rb:5:in <code>&lt;top (required)&gt;'
        from /home/ec2-user/environment/koluku/config.ru:3:in </code>require_relative'
        from /home/ec2-user/environment/koluku/config.ru:3:in <code>block in &lt;main&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/builder.rb:55:in </code>instance_eval'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/builder.rb:55:in <code>initialize'
        from /home/ec2-user/environment/koluku/config.ru:in </code>new'
        from /home/ec2-user/environment/koluku/config.ru:in <code>&lt;main&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/builder.rb:49:in </code>eval'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/builder.rb:49:in <code>new_from_string'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/builder.rb:40:in </code>parse_file'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/server.rb:319:in <code>build_app_and_options_from_config'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/server.rb:219:in </code>app'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands/server.rb:84:in <code>app'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/rack-2.0.4/lib/rack/server.rb:354:in </code>wrapped_app'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands/server.rb:148:in <code>log_to_stdout'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands/server.rb:102:in </code>start'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands/commands_tasks.rb:90:in <code>block in server'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands/commands_tasks.rb:85:in </code>tap'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands/commands_tasks.rb:85:in <code>server'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands/commands_tasks.rb:49:in </code>run_command!'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/railties-5.0.6/lib/rails/commands.rb:18:in <code>&lt;top (required)&gt;'
        from /home/ec2-user/environment/koluku/bin/rails:9:in </code>require'
        from /home/ec2-user/environment/koluku/bin/rails:9:in <code>&lt;top (required)&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/spring-2.0.2/lib/spring/client/rails.rb:28:in </code>load'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/spring-2.0.2/lib/spring/client/rails.rb:28:in <code>call'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/spring-2.0.2/lib/spring/client/command.rb:7:in </code>call'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/spring-2.0.2/lib/spring/client.rb:30:in <code>run'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/spring-2.0.2/bin/spring:49:in </code>&lt;top (required)&gt;'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/spring-2.0.2/lib/spring/binstub.rb:31:in <code>load'
        from /usr/local/rvm/gems/ruby-2.4.1/gems/spring-2.0.2/lib/spring/binstub.rb:31:in </code>&lt;top (required)&gt;'
        from /home/ec2-user/environment/koluku/bin/spring:15:in <code>require'
        from /home/ec2-user/environment/koluku/bin/spring:15:in </code>&lt;top (required)&gt;'
        from bin/rails:3:in <code>load'
        from bin/rails:3:in </code>&lt;main&gt;'

fog-awsはcarrierwave.rbの設定ファイルの設定が違うようです。

こちらを参考に。
https://github.com/carrierwaveuploader/carrierwave#using-amazon-s3

以下のようにしました。

CarrierWave.configure do |config|
  config.fog_provider = 'fog/aws'                        # required
  config.fog_credentials = {
    provider:              'AWS',                        # required
    aws_access_key_id:     ENV['AWS_S3_ACCESS_KEY_ID'],  # required
    aws_secret_access_key: ENV['AWS_S3_SECRET_ACCESS_KEY'],     # required
    region:                'us-east-2',                   # optional, defaults to 'us-east-1' オハイオ=us-east-2
    #host:                  's3.example.com',             # optional, defaults to nil
    #endpoint:              'https://s3.example.com:8080' # optional, defaults to nil
  }
  config.fog_directory  = '自分のバケツ名'                # required
  config.fog_public     = false                                                 # optional, defaults to true
  config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" } # optional, defaults to {}
end

設定項目についてですが、

設定 説明
region regionは、自分が使ってるAWSの管理画面にログインすればわかります。
ちなみに米国東部(オハイオ)はus-east-2でした。
host hostはコメントアウト
endpoint endpointもコメントアウト
config.fog_public trueにすると直リンクで画像にアクセスできるが、falseにすると認証文字列付きURLじゃないとアクセスできないらしい。
config.fog_attributes これは画像のキャッシュ日数。ブラウザにキャッシュさせる日数なので多めに365日で指定しました。S3へのアクセスは課金が発生するのでなるべくキャッシュに入れておくほうがよいと思いました。

自分のregionを確認するにはAWSにログインしてURLを見る。

設定完了、画像アップロードしてみます

ここまで参考サイトを見ながら設定を進めてきました。サイトによっては簡単、説明見ながら10分で出来る♪なんて書いてあるサイトがあったんですけど、僕の環境ではまる1日かかってしかも自分では解決できなくてテックアカデミーの講師に聞いてようやく解決したのです。(´;ω;`)

さてプロフィール画像のアップロードを試してみます。

いきなりエラー出ました(´Д⊂グスン

Excon::Error::Forbidden (Expected(200) <=> Actual(403 Forbidden) excon.error.response :body => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>SignatureDoesNotMtch</Code><Message>The request signature we calculated does not match the signature you provide. <span class="box-yellow">Check your Key</span> and signing method.</Message>

でもこのエラーはすぐ理解できました。
環境変数に入れた

AWS_S3_ACCESS_KEY_IDとAWS_S3_SECRET_ACCESS_KEYが間違えていたようです。
これを修正して、画像アップロード。

できました♪

ちなみにUserテーブルのプロフィール画像のカラムにはランダムで生成したファイル名が記述されています。

あとはherokuにデプロイしてからちゃんと画像アップロードが出来るかのチェックです。