はじめに
前回は、Herokuのコマンドの拡張pluginについて解説しました。ちょっとマニアックだったかと思いますが、Herokuのオープンであるというビジョンが見える非常に面白い仕組みだと思います。今回は、マンガ共有サービスの機能を追加したいと思います。Webサービスでは必須の機能のファイルアップロードの仕組みをHerokuを利用する場合どのように実現するかを紹介します。
Herokuでのファイルの扱い
Heroku上では、ファイルをサーバ上に保存するということができません。これは、以下の理由からそのようになっていると思います。
- Webサーバのインスタンスがどのサーバで動くかが特定できない
- 複数インスタンスが動いた場合に、同じファイルシステムを共有できない
Herokuでは、マルチインスタンスのプラットホームであるためこのような課題が出てきます。この問題をおそらく解決することはできるかもしれませんが、Herokuではアクセスを不可にしています。ファイルシステムを扱うことは他のサービスと組み合わせて利用することで代替が可能です。そうすることで、Herokuはシンプルな構成を維持できるのだと思います。
Herokuでファイルアップロードを実現するためにはファイルをサーバのファイルシステムに保存するのではなく、別の場所に保存するようにしないといけません。一般的にHerokuでファイルを保存する際に利用されるのは、Amazon S3ではないでしょうか?
Amazon S3は、Amazonが提供しているファイルストレージサービスです。S3を利用するには、Amazon Web Serviceの登録が必要です。非常に堅牢で安価なので、多くのサービスがAmazon S3を利用してファイルの保存や配信を行っています。
今回は、そのAmazon S3を利用してファイルアップロードを実現する方法を紹介します。マンガ共有サービスとしては、マンガに自分の好きな写真を載せられるという機能とします。こちらにアクセスして動作を試してみてください。マンガの編集画面からファイルアップロードができるようになっています。
carrierwaveでファイルアップロード
Ruby on Rails でファイルアップロードを実現するために、いくつかのgemがあります。代表的なものとして、以下の3つが上がります。
今回はこの中でも一番新しく出てきたcarrierwaveというgemを利用してファイルアップロードを実装します。まずは、ファイルをアップロードする部分を標準のローカルファイルシステムに保存する形で機能を実現します。
Gemfileに以下の行を追加します
gemを追加したので、bundlerを利用してgemをインストールします。
まずは、carrierwaveを利用するためにファイルをgenerateします。以下のコマンドを発行すると、ファイルアップロード用のクラスが生成されます。
既存のmangaクラスに画像を追加する形にするために、mangasテーブルにimageカラムを追加します。
マイグレーションファイルが生成されるので、マイグレーションを実行します。
次に、mangaクラス(app/models/manga.rb)にアップロードファイルをひもづけるためのコードを追加します。
attr_accessibleに、:imageを追加して、フォームから登録が可能にします。mount_uploaderメソッドを実行してクラスにアップロードファイルを紐付けます。
最後に、編集のための入力フォーム(app/views/mangas/_form.html.haml)を修正します。
form_forメソッドに :html => {:multipart => ture} の引数を追加してformタグをマルチパートにしてファイルアップロード可能にします。f.file_field :imageを追加してファイルアップロードの入力ボックスを追加します。
以上の変更点は、こちらのコミットになっています。carrierwaveを用いることで非常に簡単にファイルアップロードが実現できました。
保存先をAmazon S3に
続いて、Herokuで利用できるようにAmazon S3を利用するように変更していきましょう。carrierwaveで保存先をS3に指定するためには、fogというgemを利用します。このgemはAmazon S3に限らず様々なクラウドのサービスをRubyから利用しやすくしてくれるライブラリです。carrierwaveでは、S3を利用するためにこのgemを使うので以下の行をGemfileに追加してfogを利用できるようにします。
gemを追加したので、bundlerを実行します。
carrierwaveでS3を利用するために設定ファイルを追加して、設定を書きます。config/initializers/carrierwave.rbというファイルを以下の内容で作成します。
Herokuでは、鍵の情報や環境ごとに異なる設定などはENVに登録するとコマンドと連携して設定しやすいのでENVから読み出すようにしました。以下のコマンドを発行すれば、ENVを設定することができます。
KEY_IDとSECRET_KEYは、Amazon Web Serviceの「セキュリティー証明書」のページからさ取得できます。BUCKETは、S3の管理画面からbucketを作成しその名前を設定します。
次に、ImageUploaderクラス(app/uploaders/image_uploader.rb)を編集して、ファイルアップロード先をS3に指定します。storageメソッドの引数に:fileが指定されていますが、以下のように:fogを指定するようにします。
また、最終的にはファイルはS3に保存されるのですが、一旦はHerokuのインスタンスで受け取る形になります。そのような場合のために、app/tmpディレクトリは一時的に書き込み可能になっています。carrierwaveの初期設定でファイルを一旦保持するためのディレクトリが別のディレクトリになっているので、以下のメソッドを定義して一時保存先をtmpディレクトリ以下に変更します。
これで保存先をS3に変更できました。非常に簡単ですね。この保存先の変更のコミットはこちらになっています。
まとめ
carrierwaveとAmazon S3を利用することで、ファイルの保存に制限のあるHerokuでも、これほど簡単にファイルアップロード機能が実現できました。Herokuがマルチテナントのプラットホームであるがゆえの制限ですが、これほど簡単に問題が解決できるようになっているのであれば制限というほどではないと考えられるのではないでしょうか。
次回は、Webサービスで一般的に利用されるであろう非同期のサーバでの処理をHerokuで実現する方法を紹介します。