ククログ

株式会社クリアコード > ククログ > Railsで画像アップロード

Railsで画像アップロード

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」リンクから画像アップロードフォームへ進みます。

indexページ

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