はじめに
プログラミング学習を初めて3ヶ月程度の初心者ですが、Railsチュートリアルで学んだことを備忘録として残したいと思います。この記事は、Railsチュートリアルを参考に書きました。
Active Storageとは
Railsでファイルをアップロードするのに最も便利な方法は、Railsに組み込まれているActive Storageという機能を用いることです。これを使えば、フォームで画像の投稿機能などが簡単に作れます。
アプリケーションでActive Storageを用いることで、ImageMagickで画像のアップロードを変換したり、 PDFやビデオなどの非画像アップロードの画像表現を生成したり、任意のファイルからメタデータを抽出したりできます。
Active Storageを使えるようにする
$ rails active_storage:install
$ rails db:migrate
このマイグレーションにより、active_storage_blobs
とactive_storage_attachments
という名前の2つのテーブルが生成される。`active_storage_blobsは実際にアップロードしたファイルが保存されるテーブルで、
active_storage_attachments`は中間テーブルになります。
1つのファイルを追加する
今回は、Micropostモデルに1つの画像を追加したとします。
class Micropost < ApplicationRecord
has_one_attached :image
end
:imageはファイル名で、:photo、:avatarなど、ファイルの用途に合わせて好きなものをして下さい。
has_one_attached
メソッドは、指定のモデルと、アップロードされたファイルを関連付けるのに使います。この場合はimageを指定してMicropost
モデルと関連付けます。
次に、マイクロポストのフォームにfile_fiele
タグを追加します。
<%= form_with model: @micropost, local: true do |f| %>
<%= f.text_area :content %>
<%= f.file_field :image %>
<%= f.submit %>
<% end %>
最後に、Microposts
コントローラを更新して、新たに作成したmicropost
オブジェクトに画像を追加できるようにします。Actuve Storage API
にはそのためのattach
メソッドが提供されていますので、これを使って行えます。具体的には、Microposts
コントローラのcreate
アクションの中で、アップロードされた画像を@micropost
オブジェクトにアタッチします。このアップロードを許可するために、micropost_params
メソッドを更新して:image
を許可済み属性リストに追加し、Web経由で更新できるようにする必要もあります。
class MicropostsController < ApplicationController
def create
@micropost = current_user.microposts.build(micropost_params)
@micropost.image.attach(params[:micropost][:image])
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
private
def micropost_params
params.require(:micropost).permit(:content, :image)
end
end
一度画像がアップロードされれば、micropostパーシャルのimage_tag
ヘルパーを用いて、関連付けられたmicropost.image
を描画(レンダリング)できるようになります。また、投稿は画像を添付されていたり(画像の無いテキストのみ等)、されていなかったりするため、attached?
という論理値を返すメソッドを使って、画像が添付されている投稿なのか、添付されていない投稿かを調べ、画像が添付されていたら、画像を表示するようにします。
<%= image_tag micropost.image if micropost.image.attached? %>
画像の検証
上記のアップローダーには、いくつかの目立つ欠点があります。特に、アップロードされた画像に対する制限がないため、もしユーザーが巨大なファイルを上げたり、無効なファイルを上げると問題が発生してしまいます。この欠点を直すために、画像サイズやフォーマットに対するバリデーションを実装します。
Active Storageは、こうしたフォーマット機能やバリデーション機能がネイティブでサポートされていません。なのでActive Storageバリデーション用のgemを追加します。
gem 'active_storage_validations'
忘れずに、bundle installを実行します。
$ bundle install
このgemのドキュメントを読んでみると、次のようにcontent_type
を検査することで画像をバリデーションできることがわかります。
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" }
今回は、画像のサイズを5MBに制限します。
これらのバリデーションをMicropostモデルに追加します。
class Micropost < ApplicationRecord
validates :image, 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" }
end
また、これらのバリーデーションに加え、クライアント側(つまりブラウザ)にも、画像アップロードのサイズやフォーマットをチェックする仕組みを追加します。JavaScript
(具体的にはjQuery
)をちょっぴり加えて、ユーザーがアップロードしようとする画像が巨大過ぎるときにアラートを表示してみましょう(こうしておけばユーザーがアップロードで無駄な時間を使わずに済みますし、サーバーの負荷も軽くなります)。
<script type="text/javascript">
$("#micropost_image").bind("change", function() {
var size_in_megabytes = this.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert("Maximum file size is 5MB. Please choose a smaller file.");
$("#micropost_image").val("");
}
});
</script>
これにより、もしファイルサイズが大きすぎた場合、alert
メソッドで警告を出します。
最後に、accept
パラメータをfile_field
入力タグで用いれば、有効なフォーマットでないとアップロードできないことをユーザーに伝えられるようになります。
<%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %>
最初に有効な画像フォーマットだけを選択可能にしておき、それ以外のファイルタイプを灰色で表示します。
画像のリサイズ
画像をリサイズするためには、画像を操作するプログラムが必要になります。今回はImageMagick
というプログラムを使うので、これを開発環境にインストールします。(本番環境がHeroku
であれば、既に本番環境で`ImageMagickが使えるようになっています。)
クラウドIDEでは、次のコマンドでこのプログラムをインストールできます。
$ sudo apt-get -y install imagemagick
クラウドIDEや同等のLinux環境を使っていない場合、それぞれの環境に応じてImagiMagickをインストールする手順が変わります。例えばMacの場合であれば、Homebrewを導入しておかないとbrew install imagemagickコマンドでインストールできません。
Homebrewの場合のインストール方法
$ brew install imagemagick
次は、画像処理のためにいくつかのgemが必要です。
image_processing gem
や、Ruby製ImageMagickプロセッサであるmini_magick gem
などを必要とします。
gem 'image_processing'
gem 'mini_magick'
$ bundle install
おそらく、インストール後にRailsサーバー
の再起動が必要になると思います。
variatメソッド
Active Storageが提供するvariant
メソッドで変換済み画像を作成できるようになります。特にresize_to_limit
オプションを用いて、以下のように画像の幅や高さが500ピクセルを超えることのないように制約をかけます。
image.variant(resize_to_limit: [500, 500])
このコードは別途display_imageメソッドに置いて利便性を高めておきます。
# 表示用のリサイズ済み画像を返す
def display_image
imge.variant(resize_to_limit: [500, 500])
end
これでdisplay_imageをmicropostパーシャルで使えるようになります。
<%= image_tag micropost.display_image if micropost.image.attached? %>