Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

RailsとShrineで実現する柔軟なファイルアップロードの完全ガイド

Ruby on Railsには、CarrierWave、Paperclip、Dragonflyなど、数多くのファイルアップロード用gemが存在します。それぞれに特徴があり、少なくとも1つは使ったことがある方も多いのではないでしょうか。

本記事では、Janko Marohnić氏によって開発された比較的新しいながらも非常に優れたソリューション「Shrine」をご紹介します。他の類似gemと異なるのは、そのモジュール方式の設計です。すべての機能が個別のモジュール(Shrineでは「プラグイン」と呼ばれます)として提供されています。バリデーションが必要ならプラグインを追加、画像処理が必要ならプラグインを追加——この柔軟なアプローチにより、モデルごとに必要な機能だけを自由に組み合わせられる点が大きな魅力です。

本記事で学べる内容は以下のとおりです。

  • RailsアプリケーションへのShrineの導入方法
  • グローバル設定とアップローダー単位の設定
  • ファイルアップロード機能の実装
  • ファイルの加工(リサイズなど)
  • バリデーションルールの追加
  • メタデータの保存とAmazon S3を使ったクラウドストレージの活用

なお、本記事のソースコードはGitHubで公開されており、動作するデモも用意されています。

Shrineの導入

まずは、デフォルトのテストスイートなしで新しいRailsアプリケーションを作成しましょう。

このデモではRails 5を使用しますが、ここで紹介する概念のほとんどはRails 3や4でも同様に適用できます。

GemfileにShrine gemを追加します。

次に以下のコマンドを実行します。

続いて、Photoという名前のモデルを作成します。Shrineはファイルに関するすべての情報を、_dataという接尾辞が付いた特殊なテキストカラムに保存します。対応するマイグレーションを作成して適用しましょう。

古いバージョンのRailsでは、マイグレーションコマンドは以下になります。

初期設定

Shrineの設定は、グローバルとモデルごとの両方で行えます。グローバル設定はもちろんイニシャライザーファイル内に記述します。ここでは必要なファイルとプラグインを読み込みます。Shrineにおけるプラグインは、機能を独立したモジュールとして切り出す仕組みで、利用可能な機能を完全にコントロールできます。例えば、バリデーション、画像処理、添付ファイルのキャッシュなど、さまざまなプラグインが用意されています。

まずは2つのプラグインを追加します。1つはActiveRecordのサポート、もう1つはロギングの設定です。これらはグローバルに読み込まれます。あわせてファイルシステムストレージも設定しましょう。

config/initializers/shrine.rb

ロガーは、ファイル処理にかかった時間などのデバッグ情報をコンソールに出力してくれます。開発時に非常に便利です。

アップロードされたファイルはすべてpublic/uploadsディレクトリ内に保存されます。これらのファイルをGitで管理したくないので、除外設定を行いましょう。

.gitignore

次に、モデル固有の設定を記述するための特別な「アップローダー」クラスを作成します。現時点では空のクラスで構いません。

models/image_uploader.rb

最後に、このクラスをPhotoモデルに組み込みます。

models/photo.rb

[:image]は、フォーム作成時に使用される仮想属性を追加します。上記の行は、以下のように書き換えることも可能です。

これでモデルがShrineの機能を持つようになりました。次のステップに進みましょう。

コントローラー・ビュー・ルーティング

このデモでは、写真を管理するためのコントローラーを1つだけ用意します。indexページをルートパスとして設定します。

photos_controller.rb

ビューは以下のとおりです。

views/photos/index.html.erb

@photos配列を描画するために、パーシャルが必要です。

views/photos/_photo.html.erb

image_data?は、レコードが画像を持っているかどうかを判定するActiveRecord由来のメソッドです。

image_urlはShrineのメソッドで、元画像へのパスを返します。もちろん、表示には小さなサムネイルを使う方が望ましいのですが、それは後ほど対応します。

必要なルーティングをすべて追加します。

config/routes.rb

これで下準備は完了です。ここからが本番です!

ファイルのアップロード

このセクションでは、実際にファイルをアップロードする機能の追加方法を解説します。コントローラーのアクションは非常にシンプルです。

photos_controller.rb

唯一の注意点は、Strong Parametersではimage_dataではなく、仮想属性であるimageの方を許可する必要があるという点です。

photos_controller.rb

newビューを作成します。

views/photos/new.html.erb

フォームのパーシャルもごくシンプルです。

views/photos/_form.html.erb

繰り返しになりますが、ここでもimage_dataではなくimage属性を使用している点に注意してください。

最後に、エラー表示用のパーシャルを追加します。

views/shared/_errors.html.erb

これで基本的な準備は整いました。すぐに画像のアップロードを始められます。

バリデーション

とはいえ、デモアプリを完成させるにはまだやるべきことがあります。最大の問題は、ユーザーが任意の種類・任意のサイズのファイルをアップロードできてしまうことです。これはあまり好ましくありません。そこで、バリデーションをサポートするプラグインを追加しましょう。

config/initializers/shrine.rb

ImageUploaderにバリデーションロジックを設定します。

models/image_uploader.rb

ここでは、1MB未満のJPGおよびPNG画像のみアップロードを許可しています。ルールは用途に応じて自由に調整してください。

MIMEタイプの判定

もう1つ重要なポイントがあります。デフォルトでは、ShrineはHTTPヘッダーのContent-Typeを使ってファイルのMIMEタイプを判定します。しかし、このヘッダーはブラウザが送信するもので、ファイル拡張子のみに基づいて設定されるため、必ずしも信頼できるとは限りません。

ファイルの中身に基づいてMIMEタイプを判定したい場合は、determine_mime_typeプラグインを使用します。他のモデルではこの機能が不要な場合もあるため、アップローダークラス側に組み込みます。

models/image_uploader.rb

このプラグインは、デフォルトでLinuxのfileユーティリティを使用します。

添付画像のキャッシュ

現在の実装では、ユーザーが不正なデータを含むフォームを送信すると、エラー付きでフォームが再表示されます。しかし問題なのは、添付された画像が失われ、ユーザーが再度ファイルを選択し直さなければならない点です。これはcached_attachment_dataというプラグインを使えば簡単に解決できます。

models/image_uploader.rb

あとはフォームに隠しフィールドを追加するだけです。

views/photos/_form.html.erb

写真の編集

これで画像をアップロードできるようになりましたが、編集機能がありません。早速修正しましょう。対応するコントローラーのアクションはごく標準的なものです。

photos_controller.rb

同じ_formパーシャルを再利用します。

views/photos/edit.html.erb

しかしこれだけでは不十分です。ユーザーはまだアップロード済みの画像を削除できません。これを可能にするには、そう、またしてもプラグインが必要です。

models/image_uploader.rb

このプラグインは:remove_imageという仮想属性を使用するため、コントローラーで許可します。

photos_controller.rb

そして、レコードに添付ファイルが存在する場合に削除用チェックボックスを表示します。

views/photos/_form.html.erb

サムネイル画像の生成

現状では元画像をそのまま表示していますが、プレビューとしては最適とは言えません。写真は大きく、画面スペースを占有しがちだからです。CSSのwidthheightで縮小表示することもできますが、それも良い方法ではありません。スタイルで小さく見せても、ユーザーは元の大きなファイルをダウンロードする必要があるからです。

そこで、初回アップロード時にサーバー側で小さなプレビュー画像を生成する方がはるかに効率的です。これには2つのプラグインと2つの追加gemが必要です。まずgemを追加しましょう。

image_processingはShrineの作者が作成した特別なgemで、画像操作のための高レベルなヘルパーメソッドを提供します。このgemは、ImageMagickのRubyラッパーであるmini_magickに依存しています。つまり、このデモを実行するにはシステムにImageMagickがインストールされている必要があります。

新しいgemをインストールします。

次に、依存関係とともにプラグインを読み込みます。

models/image_uploader.rb

processingプラグインは、画像の縮小・回転・フォーマット変換などの操作を可能にします。そしてversionsプラグインにより、1つの画像を複数のバリアントとして保持できます。このデモでは、「original」と「thumb」(300x300にリサイズ)の2つのバージョンを保存します。

画像を処理して2つのバージョンを保存するコードは以下のとおりです。

models/image_uploader.rb

resize_to_limit!はimage_processing gemが提供するメソッドです。画像が300x300より大きい場合は縮小し、小さい場合は何もしません。さらに、元のアスペクト比は維持されます。

画像を表示する際は、image_urlメソッドに:originalまたは:thumb引数を渡すだけです。

views/photos/_photo.html.erb

フォーム内でも同様に行えます。

views/photos/_form.html.erb

アップロード完了後に処理用の一時ファイルを自動的に削除したい場合は、delete_rawプラグインを追加します。

models/image_uploader.rb

画像のメタデータ

画像の表示だけでなく、そのメタデータを取得することもできます。例として、元画像のサイズとMIMEタイプを表示してみましょう。

views/photos/_photo.html.erb

では、画像の寸法はどうでしょうか?残念ながらデフォルトでは保存されませんが、store_dimensionsプラグインを使えば可能です。

画像の寸法

store_dimensionsプラグインはfastimage gemに依存しているため、先に追加しておきましょう。

忘れずに以下を実行してください。

あとはプラグインを読み込むだけです。

models/image_uploader.rb

そして、widthheightメソッドを使って寸法を表示します。

views/photos/_photo.html.erb

また、幅と高さを配列で返すdimensionsメソッドもあります(例:[500, 750])。

クラウドストレージへの移行

開発者はアップロードファイルのホスティングにクラウドサービスを選ぶことが多く、Shrineにもその機能が備わっています。このセクションでは、Amazon S3へのファイルアップロード方法を紹介します。

最初のステップとして、Gemfileに2つのgemを追加します。

aws-sdkはS3 SDKを扱うために必要であり、dotenv-railsは開発環境での環境変数管理に使用します。

作業を進める前に、S3へAPI経由でアクセスするためのキーペアを取得する必要があります。Amazon Web Servicesコンソールにサインイン(またはサインアップ)し、Security Credentials > Usersへ移動してください。S3上のファイルを操作する権限を持つユーザーを作成します。以下はS3へのフルアクセスを許可するシンプルなポリシーの例です。

作成したユーザーのキーペアをダウンロードしてください。rootアクセスキーを使うことも可能ですが、セキュリティ上のリスクが非常に高いため、強く非推奨とします。

次に、ファイルをホストするためのS3バケットを作成し、プロジェクトのルートに設定ファイルを配置します。

.env

このファイルは絶対に公開してはいけません。Gitの除外設定も必ず行ってください。

.gitignore

続いて、Shrineのグローバル設定を変更し、新しいストレージを導入します。

config/initializers/shrine.rb

以上で完了です!アプリの他の部分を変更する必要はなく、すぐに新しいストレージをテストできます。S3からキーが正しくないというエラーが返される場合は、キーとシークレットを末尾の空白や不可視の特殊文字を含めずに正確にコピーしたか確認してください。

まとめ

本記事は以上です。ここまで読んでいただければ、Shrineを使いこなす自信がつき、自分のプロジェクトでぜひ活用してみたいと思っていただけたのではないでしょうか。今回は多くの機能を取り上げましたが、Shrineにはファイルと一緒に追加のコンテキストを保存する機能や、ダイレクトアップロードの仕組みなど、さらに多くの機能が備わっています。

すべての利用可能なプラグインについて詳しく知りたい方は、Shrineの公式ドキュメントや公式サイトをぜひ参照してください。このgemについて他に疑問があれば、遠慮なく質問してください。最後までお読みいただきありがとうございました。またお会いしましょう!

  1. RailsでTailwind CSSを使う方法|導入から実践的なスタイリングまで徹底解説

    CSSは魔法のような存在ですが、同時に時間のかかる作業でもあります。美しく、機能的で、アクセシブルなサイトは使っていて心地よいものですが、自分でCSSを一から書くのは骨の折れる仕事です。近年はBootstrapをはじめとする多くのCSSフレームワークが登場し、その中でもTailwind CSSは特に注目を集めています。 RailsにはTailwindが標準搭載されていませんが、この記事では新しいRuby on RailsプロジェクトにTailwind CSSを追加する方法を解説します。これにより、デザイン実装にかかる時間を大幅に節約できるでしょう。さらに、Tailwindのユーティリティクラス

  2. Rails5でのAngularの使用

    あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた