Railsで画像をアップロードするときはどうやっているんでしょうか。
Fleximageというプラグインがあります。よいAPIだと思うのですが、あまり使っている人がいないようなので紹介します。
ここでは1からサンプルRailsアプリケーションを作成しながらFleximageの使い方を紹介します。順番にコマンドを実行・コードを変更していくと動くように書いてあります。
下準備
まず、sampleというRailsアプリケーションを作成します。
% cd /tmp
% rails sample
% cd sample
次に、Fleximageをインストールし、画像用のテーブルを作成します。
% script/plugin install git://github.com/Squeegy/fleximage.git
% script/generate scaffold photo title:string image_filename:string image_width:integer image_height:integer
% rake db:migrate
以下のカラムは特別なカラムです。Fleximageは以下のカラムが定義されていると、そこに値を設定してくれます。
-
image_filename
-
アップロードした画像のファイル名
-
image_width
-
画像の幅
-
image_height
-
画像の高さ
これらはすべて省略可能です。今回は、せっかくなので、すべて定義しました。
Fleximageを使うため、モデルを以下のように変更します。
app/models/photo.rb:
class Photo < ActiveRecord::Base
acts_as_fleximage :image_directory => 'public/images/uploaded'
end
これで、アップロードされた画像は"#{RAILS_ROOT}/public/images/uploaded"以下に保存されます。
ここからは、コードを変更し、アプリケーションを動かし、動作を確認しながら進めていきます。そのため、ここでサーバを起動しておきます。
% script/server
アップロード
http://localhost:3000/photos/にアクセスすると以下のような見慣れたscaffoldの画面になるので、「New Photo」リンクから画像アップロードフォームへ進みます。
scaffoldのフォームでは画像をアップロードできないので、app/views/photos/new.htmlを以下のように変更します。
app/views/photos/new.html:
<h1>New photo</h1>
<% form_for(@photo, :html => {:multipart => true}) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :image_file %><br />
<%= f.file_field :image_file %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', photos_path %>
これで、フォームは以下のようになります。
タイトルとアップロードする画像のパスを指定して「Create」ボタンを押します。
画像がアップロードされ、ファイル名やサイズが設定されていることが確認できます。
せっかく画像をアップロードしたので、アップロードした画像も表示するようにします。app/views/photos/show.html.erbに以下を追加します。ここでは、タイトルの下に追加しました。
app/views/photos/show.html.erb:
<p>
<%= image_tag(photo_path(@photo, :format => "png")) %>
</p>
PNGフォーマットを指定しているので、コントローラ側でPNGフォーマットを受けつけるようにします。
app/controllers/photos_controller.rb:
def show
@photo = Photo.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @photo }
format.png # <- 追加
end
end
PNGフォーマットのビューを作ります。ここが、FleximageのAPIのよいところです。
Fleximageを使ったビューは「#{アクション名}.#{フォーマット}.flexi」というファイル名になります。今回の場合だと、「show.png.flexi」になり、以下のような内容になります。
app/views/photos/show.png.flexi:
# -*- ruby -*-
@photo.operate do |image|
end
これで、http://localhost:3000/photos/1.pngで画像を表示することができるようになりました。画像表示ページは以下のようになります。
サムネイル
画像表示ページではアップロードされた画像をそのまま表示しました。画像一覧ページでは画像のサムネイルを表示することにします。
サムネイル画像はhttp://localhost:3000/photos/1/thumbnail.gifで表示するようにします。そのため、まず、config/routes.rbを変更します。
config/routes.rb:
map.resources :photos, :member => {:thumbnail => :get}
これで、thumbnail_photo_pathが使えるようになるので、index.html.erbを書き換えます。
app/views/photos/index.html.erb:
<h1>Listing photos</h1>
<table>
<tr>
<th>Title</th>
<th>Thumbnail</th>
<th colspan="3">Action</th>
</tr>
<% @photos.each do |photo| %>
<tr>
<td><%=h photo.title %></td>
<td><%= image_tag(thumbnail_photo_path(photo, :format => "gif") %></td>
<td><%= link_to 'Show', photo %></td>
<td><%= link_to 'Edit', edit_photo_path(photo) %></td>
<td><%= link_to 'Destroy', photo, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
<br />
<%= link_to 'New photo', new_photo_path %>
ここでは意味もなくフォーマットにgifを指定していますが、これはFleximageが画像フォーマットを簡単に変更できることを示すためです。
コントローラにGIFフォーマットに対応したthumbnailアクションを定義します。
app/controllers/photos_controller.rb:
def thumbnail
@photo = Photo.find(params[:id])
respond_to do |format|
format.gif
end
end
サムネイルのGIF画像用のビューは以下のようになります。とてもすっきり書けています。
app/views/photos/thumbnail.gif.flexi:
# -*- ruby -*-
@photo.operate do |image|
image.resize('80x60')
end
変更した一覧ページを表示するとこうなります。
アップロードした画像がサムネイルとして表示されています。
まとめ
すっきりしたAPIのRails用画像アップロードプラグインFleximageを紹介しました。
ここでは触れませんでしたが、Fleximageは機能が豊富でカスタマイズ性にも優れています。例えば、アップロードされた画像を表示するときに影をつけたり、文字を入れたりといろいろ加工することができます。また、ファイルではなく、データベースに画像を保存することもできます。
Fleximageは日本語の情報がほとんどないのが不思議ですね。あまり使われていないのかもしれません。
おまけ
ImageMagickを使って画像に影をつけるシェルスクリプトです。これを使うと、このページのスクリーンショットについているような影をつけることができます。
add-shadow.sh:
#!/bin/sh
if [ $# != 2 ]; then
echo "Usage: $0 INPUT SHADOWED_OUTPUT"
exit 1
fi
convert $1 \( +clone -background black -shadow 80x6 \) \
+swap -background none -layers merge +repage $2
このように使います。
% ./add-shadow.sh image.png shadowed-image.png