この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
Amazon S3互換のストレージサーバーRiak CSに、Railsの画面からファイルをアップロードする機能を実装します。
Riak CSについて
Riak CSは、 Basho Technologies, Inc. がオープンソースで開発しているAmason S3互換のストレージサーバーです。Railsアプリケーションでは、carrierwaveとfog-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_confirm
, update_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に保存するタイミングで実際の保存用ディレクトリにアップロードし直され、自動的に削除されます。
まとめ
carrierwave
とfog-aws
を使うことにより、Amazon S3だけでなく互換性のあるストレージシステムに対しても簡単にファイルをアップロードする機能を実装することができます。
ただし、使用されているソフトウェアによって若干の差異があるため、実際に動かして確認する必要があります。