62日目
今日の学習
Ruby on Rails
Rails チュートリアル第13章
第13章の〆として、画像を投稿できるようにしていく。
マイクロポストの画像投稿
画像付きマイクロポストを投稿できるようにするに当たって、開発環境用のβ版を実装して、改善を施してから本番環境用の完成版を実装するという流れになる。
画像アップロード機能のために必要な視覚的要素は以下の二点。
- 画像をアップロードするためのフォーム
- 投稿された画像
Active Storage
Railsでファイルをアップロードするために、Railsに組み込まれているActive Storage
という機能を利用する。
これは画像のみならず、PDFや音声ファイルも扱えるようだ。
まず、以下のコマンドでActive Storage
をアプリケーションに追加する。
rails active_storage:install
上記のコマンドを実行すると、添付ファイルの保存に用いるデータモデルを作成するためのデータベースマイグレーションが生成されるため、マイグレーションを実行すること。
インストール後使えるメソッドで、最初に使う必要があるものはhas_one_attached
メソッド。
has_one_attatched
メソッドは、指定のモデルとアップロードされたファイルの関連付に使用する。
今回はMicropost
モデルと画像のimage
ファイルを紐づけるので、以下のようにする。
app/models/micropost.rb
has_one_attached :image
has_one_attached
では1つのファイルを添付できるようになっているが、has_many_attached
を使うと複数のファイルが添付できるようになる。Railsチュートリアルで作成するものは、マイクロポスト1つにつき画像は1つという設計をしているので、今回は前者のオプションを利用する。
次に、マイクロポストのフォーム部分にファイルを添付できるようにfile_field
タグを追加する。
ビューの編集ができたら、最後にMicroposts
コントローラを更新し、micropost
オブジェクトに画像が追加できるようにする。
Active Strage API
は、attach
メソッドを用意しており、これを使って行う。また、micropost_params
のpermit
に:image
を追加し、画像を許可する必要もある。
app/controllers/microposts_controller.rb
def create @micropost = current_user.microposts.build(micropost_params) @micropost.image.attach(params[:micropost][:image]) ... ... def micropost_params params.require(:micropost).permit(:content, :image) end
画像の投稿ができるようになったので、マイクロポストで投稿された画像が見られるようにする。
image_tag
ヘルパーを用いて、micropost.image
を描画する。
画像の投稿がない場合は、画像を表示させないようにするためにattached?
という論理値を返すメソッドを使う。
app/views/microposts/_micropost.html.erb
<%= image_tag micropost.image if micropost.image.attached? %>
この項目の、画像アップロードをテストするためのテンプレートを作成する演習で少しつまづいた。
assert_select 'input[type= ???]'
で、???
のところに何を埋めればファイル添付機能が実装されていることがテストできるだろうか。
こちらのサイトでinput
のtype属性を一通り確認し、その中でfile
が最も適切だったためfile
で埋めてみたところ、テストが通った。
画像が表示されているかをチェックするためには、コントローラのインスタンス変数にアクセスできるassigns
メソッドを利用する。
今回の場合は、@micropost
があるかどうかを確かめたいので、以下のようなコードになる。
assert assigns(:micropost).image.attached?
画像ファイルのバリデーション
今までの工程で実装したアップローダーを使って実際に画像を投稿してみると、リサイズされず素の状態で投稿されてしまうため非常によろしくない。もしもユーザーが巨大なファイルをあげたり、無効なファイルをあげたりすると問題が発生する。
これを解決するために、画像サイズやフォーマットに対するバリデーションを実装する。
Active Strage
では、フォーマットやバリデーション機能がサポートされていないので、新しくactive_storage_validations
といったgemを追加する。
このgemを使うと、content_type
を利用して画像のバリデーションを設定できる。また、size
を利用してファイルサイズもバリデーションできる。
content_type: { in: %w[image/jpeg image/gif image/png], message: "must be a valid image format" } size: { less_than: 5.megabytes, message: "should be less than 5MB" }
このようなバリデーションを、フロント側からもチェックできるようにする。
Railsチュートリアルでは、jQuery
でファイルサイズをチェックしている。もしもユーザーがアップロードしようとする画像サイズが巨大すぎる場合、アラートを表示するようにする。
<script type="text/javascript"> $("#micropost_image").bind("change", function() { var size_in_megabytes = this.file[0].size/1024/1024; if (size_in_megabytes > 5) { alert("Maximum file size is 5MB. Please choose a smaller file."); $("#micropost_image").val(""); } }); </script>
CSS idのmicropost_image
を含んだ要素を見つけ出して、この要素を監視する。このCSS idを持つ要素が変化したとき、このjQuery
の関数が動き出す。
実際に5MB以上の画像をアップロードしようとしてみたところ、メッセージが表示される。
最後に、file_filed
タグにaccept
パラメータを用いて、有効なフォーマットでないとアップロードできないと視覚的に分かりやすくする。
<%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %>
こうしておくことで、ユーザーがファイルをアップロードするときに有効なファイルを灰色で表示してくれるようになる。
画像以外のファイル名が灰色になっていて分かりやすく、選択もできなくなる。
しかし、フロントだけのバリデーションではあくまでアップロードしにくくするだけであり、やろうと思えばPOSTリクエストを直接発行して無効なファイルをアップロードできてしまう状態。
そのため、サーバー側のバリデーションは必ず省略せず、どちらもやる方が望ましい。
画像のリサイズ
画像サイズをリサイズするための、画像を操作するプログラムをインストールする。
以前も使用したことがあるが、ImageMagick
を利用する。他に、image_processing
gemやmini_magick
gemをインストールする。
インストール後、Active Storage
が提供するvariant
メソッドで変換した画像を作成できるようにする。
resize_to_limit
オプションを利用して、画像の幅や高さを決めることができる。
Railsチュートリアルでは、リサイズ済み画像を返してくれるメソッドdisplay_image
を作成している。
app/models/micropost.rb
def display_image # 縦横500ピクセルの制約 image.variant(resize_to_limit: [500, 500]) end
作成したメソッドを使って、リサイズ済みの画像を表示するようにする。
app/views/microposts/_micropost.html.erb
<%= image_tag micropost.display_image if micropost.image.attached? %>
リサイズされた画像が表示されない
実際に画像をアップロードして、リサイズされているかどうか確認をしてみると、画像がうまくアップロードされていない。
検証から画像のURLをコピーし、URLに飛んでみるとエラー画面に。
MiniMagick::Error in ActiveStorage::RepresentationsController#show You must have ImageMagick or GraphicsMagick installed
どうやら、ImageMagick
のインストールに失敗しており、Minimagick
が使えないということらしい。
Railsチュートリアルの指定通り、apt-get
でインストールを行ったが、そのときうまく行っていないにも関わらずログを流し読みしてスルーしてしまっていた。
エラー文に、sudo apt --fix-broken install
をしてねと書かれていたので実行。
その後、Railsチュートリアルに記載されているsudo apt-get -y install imagemagick
をもう一度実行するとインストールできた。
これで無事にリサイズされた画像が表示されるようになった。
インストールするときはちゃんと結果を確認するようにしなければならない。
本番環境での画像アップロード
実装した画像アップロード機能では、ローカルのファイルシステムに画像を保存するようになっているため、本番環境に適さない。
本番環境では、ファイルシステムではなくクラウドストレージサービスに画像を保存するようにする。
Railsチュートリアルで利用するクラウドストレージは、AWSのサービスのひとつ「S3(Simple Storage Service)」。
これは有料サービスのようだが、チュートリアル中にテストする程度であれば月に1セント(つまり1円くらい)もかからないと書いてある。
本番環境でクラウドストレージを使うために、aqs-sdk-s3
gemをインストールする。
AWSでユーザーを作成し、S3バケットを作成する。バケットとは、S3に保存される画像をはじめとしたさまざまなファイルを保管するための場所のことを指す。
このバケットを利用していくに当たって、機密にするべき情報を設定に直接書くこと(ハードコード)を避けるために、HerokuのENV
変数を使う。
heroku config:set
コマンドを使って、設定しなければならない情報を入力していく。
heroku config:set AWS_ACCESS_KEY=****** # ***にはユーザー作成時に表示された「アクセスキー ID」を入力
ここで設定したものを今の状態で使うのは危険なので、必ず環境変数ENV
変数を利用する。
本番運用するアプリケーションは、暗号化されていないIDやパスワードのような重要なセキュリティ情報は絶対にソースコードに直接書き込まない。
gemやconfigで使う設定値のyml...今回の場合、ストレージオプションにアクセスキーIDを書き込む際に、以下のような形で書く。
config/storage.yml
amazon: service: S3 # ENVを使って先程の定数を利用 access_key_id: <%= ENV['AWS_ACCESS_KEY'] %> 定義したオプションを本番環境で使うために、`Active Storage`サービス設定パラメータを`config/environments/production.rb`ファイルに追加。
storage.ymlで設定したものを読み込み
config.active_storage.service = :amazon
Herokuにデプロイ後、画像を投稿してみたがうまくいった。 次でいよいよRailsチュートリアルも最終章だ。