RailsでRmagickを利用した画像アップロード・リサイズ処理・バイナリ画像出力

Railsで画像ファイルを取り扱うためのgemは色々あるが、本記事ではRMagickのみを利用して画像管理機能とリサイズまでを行う。

ImageMagickをインストール

RMagickを利用するにはImageMagickが必要となる。MacならHomebrewをインストールして、下記のコマンドでImageMagickをインストールする。

brew versions imagemagick

CentOSRedHatなどではyumでインストールすることができる。少し古いバージョンとなるので、最新のImageMagickを利用したい場合はソースからインストールする。

yum install ImageMagick ImageMagick-devel

RMagickをインストール

Gamfileにrmagickを追加する

gem 'rmagick', require: 'RMagick'

RMagickをインストールする

bundle install

Railsでの画像アップロード

今回はバイナリデータをデータベースに保存して画像を取り扱う事を前提とする。また細かいテーブルの構造、具体的な処理のフローなどは割愛し、基本的な概念とコードのみを紹介する。

アップロード画面

Imageモデルにて画像データを取り扱うパターン。フォームの:multiparttrueにし、file_fieldにファイルを設定させる。

# images/new.html.erb
<%= form_for @image, :multipart => true do |f| %>
<%= f.file_field :image_file %>
<%= f.submit %>
<% end %>

バイナリデータの取り出し

アップロードした画像のバイナリデータはparams[:image][:image_file].tempfile.readに格納される。このデータをデータベースに保存すれば、ファイルのデータ自体は保存できるが、実際にはアップロードされた画像をWeb用に最適化したり、画像以外のものを弾く処理が必要である。

RMagickで画像のバイナリデータを扱う

アップロードされた画像のバイナリから画像を扱う場合はMagick::Image.from_blobを使う。配列は今回のケースでは一つのみなのでshiftで取り出す。

image_magick = Magick::Image.from_blob(params[:image][:image_file].tempfile.read).shift

加工が終わった画像は下記のようにバイナリで出力できる。

image_magick.to_blob

画像の向きを自動修正する

画像のEXIFのOrientationには写真の向きが設定されている。カメラやスマートフォンの向きを変えて撮影した際、この情報を埋め込む事で実際の表示時に縦向きの写真か、横向きの写真かを判別することが可能となる。

パソコンやブラウザで写真を表示しても、このOrientationを参照し正しい向きで写真を表示するが、セキュリティの観点からEXIF情報を削除したい場合やリサイズを行う場合は、この回転処理を手動で行わなければいけない。

このOrientationは下記のように取得できる。

puts image_magick.orientation

数値を取得し手動で回転処理を記述しても良いが、Rmagickにはauto_orientというのがあり、この回転処理を自動で行ってくれる。

image_magick = image_magick.auto_orient

EXIFのOrientationに様々な数値が設定されたサンプル画像がある。EXIF情報を回転処理を行わず削除した場合の挙動と、Rmagickによる回転処理が正しく行われているかのテストはこの画像を使って行うと良い。
UIImageOrientation / EXIF orientation sample images - Matt Galloway

EXIF情報を削除する

strip!で削除できる

image_magick.strip!

画像のファイルサイズを取得

画像のファイルサイズを取得するにはfilesizeを使う

if image_magick.filesize > 1000000
"1MBより大きい画像"
end

MIME Typeの取得

image_magick.mime_typeで取得することができる。params[:image][:image_file].content_typeで同じものを取得することもできるが、ファイルの拡張子によって変化する値であり、テキストファイルでも拡張子をpngとすればそれに応じて変化するようであるため、MIME Typeによって処理を変更する場合は、ImageMagickで取得できる値を利用したい。

画像のリサイズ

画像のリサイズだけでも多くの物が存在する。resize, scale, thumbnail, sampleはそれぞれ速度や画質に違いがある。下記記事でそれについての調査結果が掲載されているので参考にしたい。
RubyのRMagickで画像をリサイズする - アインシュタインの電話番号

実際のリサイズについて、画質や速度などを考慮せずに最短で実装するならresize_to_fitresize_to_fillを使う。

resize_to_fitは縦横比を保ったままリサイズを自動的に行ってくれるメソッドである。画像に対して短い方の数値を利用するようになっている。下記の例では高さ300pxが使われる。

image_magick = image_magick.resize_to_fit(3000, 300)

resize_to_fillは画像の切り抜きが行える。縦横比を維持して縮小した上で、指定されたサイズの切り抜きを実行する。

image_magick = image_magick.resize_to_fill(50, 50)

これらの処理を手動で書くのは大変な上に、細かな計算が必要となってくる。手動で実装した場合とRmagickの自動リサイズでは前者の速度が上回る傾向にあるが、頻繁な画像変換処理が発生しない場合は、自動リサイズで問題無いと思われる。

バイナリ画像出力

データベースに蓄積されたバイナリ画像をRailsで出力するにはsend_dataを利用する

# images_controller.rb
image = Image.find_by name: params[:name]
send_data image.blob, type: image.mimetype, disposition: "inline"

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA


Railsでのタイムゾーンの動的変更とサマータイムの取り扱いについて

Railsでは自分でタイムゾーンのテーブルを作成し管理することも可能だが、time_zone_selectを利用すればタイムゾーンのリストを自動生成することができ、そこで生成された値をTime.zoneに与える事でタイムゾーンを動的に変更することができる。

<%= form_for @user do |f| %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :time_zone %>
<%= f.time_zone_select :time_zone, nil, {:include_blank => true}, {class: "form-class"} %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.submit %>
<% end %>

以上のフォームでユーザー作成時にタイムゾーンを保存し、ApplicationController:before_actionなどで設定するだけで、利用者ごとにタイムゾーンを切り替える機能を構築できる。

Time.zone = user.time_zone

ユーザーのタイムゾーンによってサイトの時間表示を切り替える場合は、データベースにUTCでデータが保存されていることが前提である。ローカル時間でデータを保存していた日本国内限定サイトを多言語に対応させる場合は、一旦データベース内のdatetimeを持つレコードを取り出し、-9時間してからアップデートをかける事で対応可能である。

Time.nowではなくTime.zoneを使う

Time.nowではローカル時間に依存する。多言語対応を行う場合はTime.zone.nowを使うようにする事で、ユーザーに応じた時間を取得するよう心がけ、データベースへは基本UTCで保存されているため、Time.utc.nowを使うというようにルールを統一する。少しややこしいと思うなら、application.rbconfig.time_zoneUTCにして、適宜ユーザーに応じたタイムゾーンに変換する仕組みとした方が混乱を避ける事ができて良いだろう。

Railsサマータイムについて

  • Railstime_zone_selectではGMTによる時差付きのリストが出力される
  • データ呼び出し時にUTCに時差を加えたローカル時間を出力し、時刻の末尾にUTCの時差が表示されるRailsではサマータイムを自動で計算してくれる。

例えばイギリスのロンドンからのユーザーがアクセスした時、サマータイム中に該当する時刻のデータはUTC+01:00で取り出され、そうでない時刻に該当するデータはUTC+00:00で取り出される。8月に作成したデータのcreated_atと12月に作成したcreated_atを出力すると下記のようにサマータイムが反映されて出力される。

2014-08-31 10:00:00 +0100
2014-12-01 09:00:00 +0000

余談だが、time_zone_selectで出力されるGMTリストはサマータイムによって時差が変わるということは無い。日本にはサマータイム制度が存在しないため、サマータイムの存在を忘れて多言語対応のサイトを構築していると「GMTのリストから(GMT+00:00) Londonを選択したのに、データを取り出すと01:00になってしまうので時間がずれているのでは」と思ってしまうかもしれないが、そのような事はない。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です