その他
    ホーム 技術発信 DoRuby carrierwaveとfogでRiak CSへの画像アップロードを実装する
    carrierwaveとfogでRiak CSへの画像アップロードを実装する
     

    carrierwaveとfogでRiak CSへの画像アップロードを実装する

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    Amazon S3互換のストレージサーバーRiak CSに、Railsの画面からファイルをアップロードする機能を実装します。

    Riak CSについて

    Riak CSは、 Basho Technologies, Inc. がオープンソースで開発しているAmason S3互換のストレージサーバーです。Railsアプリケーションでは、carrierwavefog-awsという二つのGemを使うと、ファイルのアップロード処理を簡単に実装することができます。

    APIが互換なので、Railsから見るとAmazon S3とほとんど同じです。

    確認画面について

    日本の業務系アプリでは、編集画面から保存するとき「確認画面」をはさむように要求されることが多いです。これはRailsのレールから外れてしまうため、特に画像系のファイルアップロードがからむと非常に面倒です。

    しかし、carrierwaveでは《キャッシュ》というアップロードされたファイルを一時的なディレクトリに保存する機能があるので、確認画面のプレビューに画像を表示させることができます。

    複数サーバー環境

    複数のアプリケーションサーバーで負荷分散している場合、アップロードするファイルを共通のストレージに保存する必要があります。

    carrierwaveの標準の機能では、アップロードされたファイルをサーバーのディスクに直接保存するため、そのままでは使えません。

    fogという各種クラウドサービスに対応するGemを使えば、carrierwaveと連携してアップロードされたファイルをクラウドに保存することができます。

    fog-awsはAmazon S3やプロトコル互換のネットワークストレージに対応するGemです。

    今回はこのfog-awsを使用して、Riak CSにファイルをアップロードします。

    環境

    Riak CSの環境として、IDCFクラウドのオブジェクトストレージというサービスを利用させていただきました。

    Railsで作成したサンプルアプリケーションは、下記のような構成になっています。

    • Ruby 2.3.1
    • Rails 5.0.0
    • carrierwave HEAD版
    • fog-aws 0.10.0

    2016年7月の執筆時点のcarrierwaveでは、《キャッシュ》の一時保存先にfogのストレージを指定する機能がまだ正式リリースされていないため、GitHubのHEAD版を使用します。

    実装

    完成したソースコードを https://github.com/kkismd/cloud-sample に公開しています。
    以下、ポイントとなる箇所を抜粋しながら説明します。

    アプリケーションの概要

    Userというモデルが、名前(name), メールアドレス(email) という属性のほかに、avatarという名前で画像ファイルを持ち、画面からアップロードすることができる、という機能を実装しました。
    carrierwaveを使う場合、カラムとしてファイル名を表す文字列のカラムを用意します。

    create_table :users do |t|
      t.string :name
      t.string :email
      t.string :avatar
    
      t.timestamps
    end
    

    Gemfile

    前述のライブラリをGemfileに記述します。以下は抜粋です。

    ruby '~> 2.3.1'
    gem 'rails', '~> 5.0.0'
    gem 'carrierwave',
      github: 'carrierwaveuploader/carrierwave',
      ref: 'b31f7ce006bade550be0ad946d0b993b799358e3'
    gem 'fog-aws'
    
    

    イニシャライザ

    config/initializers/carrierwave.rbというファイルを作成してcarrierwaveとfog-awsの設定を記述します。

    CarrierWave.configure do |config|
      config.fog_provider = 'fog/aws'
      config.fog_credentials = {
        provider: 'AWS',
        aws_signature_version: 2,  # ☆1
        aws_access_key_id: ENV['fog_api_key'],
        aws_secret_access_key: ENV['fog_api_secret'],
        region: ENV['fog_region'],
        host: ENV['fog_host'],
      }
      config.cache_storage = :fog # ☆2
      config.cache_dir = 'tmp/image-cache'
      config.fog_directory = ENV['fog_directory']
      config.asset_host = ENV['fog_asset_host']
    end
    

    個別に設定しなければならない値は、環境変数(ENV)から読み取るようにしています。
    今回は環境変数の割り当てにfigaro というGemを使っています。

    注意しなければならないポイントとして、Riak CSではファイルアップロード時に送信する電子署名のバージョンがAmazonのものより古いため、コメント ☆1 の設定を入れなければファイルをアップロードしようとしても失敗します。この事象はエラーメッセージが分かりにくくて特定に時間がかかってしまいました。

    また前述の通り、《キャッシュ》の保存先をRiak CSに設定する ☆2 は、執筆時点でのcarrierwaveリリース版ではまだサポートされていません。

    アップローダー

    carrierwaveではまず app/uploadersディレクトリに「アップローダー」と呼ばれるクラスを定義します。ジェネレータが用意されているのでそれを利用します。

    % rails generate uploader Avatar
    

    生成したあと、保存先をfogに設定します。

    class AvatarUploader < CarrierWave::Uploader::Base
      storage :fog
    

    モデル

    つぎに、UserモデルにAvatarUploaderを《マウント》します。

    class User < ApplicationRecord
      mount_uploader :avatar, AvatarUploader
    

    コントローラ

    scaffoldでusersテーブルのCRUDを実装したあと、新規作成と保存について確認画面を追加します。
    ここでは名前を create_confirmupdate_confirm としました。
    コントローラにメソッドを記述し、routesファイルに定義を追加しました。

      resources :users do
        collection do
          post :confirm_create
        end
        member do
          patch :confirm_update
        end
      end
    

    ビュー

    確認画面でアップロードした画像をプレビューすることができますが、保存画面にデータを持ち回るために hidden フィールドにデータを保持します。

       <div class="field">
        <%= image_tag @user.avatar_url if @user.avatar? %>
        <%= f.hidden_field :avatar_cache %>
       </div>
    

    ここでイメージタグで表示される画像は、Riak CSのキャッシュ用に設定したディレクトリに保存されています。このファイルはモデルがDBに保存するタイミングで実際の保存用ディレクトリにアップロードし直され、自動的に削除されます。

    まとめ

    carrierwavefog-awsを使うことにより、Amazon S3だけでなく互換性のあるストレージシステムに対しても簡単にファイルをアップロードする機能を実装することができます。

    ただし、使用されているソフトウェアによって若干の差異があるため、実際に動かして確認する必要があります。