kentana20 技忘録

技術ネタを中心に、セミナー、勉強会、書籍、会社での出来事を綴っていきます。不定期更新。

Railsでpaperclipを使ってリサイズしつつS3へ画像をアップロードする

RubyonRailsアプリで、画像アップロード機能を作るにあたって、paperclipを試すことにしたので、技忘録。

想定するユースケース

  • エンドユーザ向け画面/管理画面で画像アップロード機能を作りたい
  • アップロードした画像はいくつかのサイズにリサイズして使うことを想定する
  • 画像はWebサーバ内部ではなく、オンラインストレージを使用して保存する
  • アップした画像はWebアプリから参照したい
  • アップロードはWebアプリからのみ許可したい
  • なるべくシンプルに作りたい

構成

これらをOSX Yosemite上で実行し、オンラインストレージにはAmazon S3を使います。

前提

  • RubyRailsは既にインストール済みであること
  • 何らかのRailsアプリが動く状態にあること
  • 画像を追加するモデル"Hoge"は既に定義されていること
  • AWSアカウントが既にあること

事前準備

画像のリサイズにはimagemagickを使うのでbrewでインストール

$ brew install imagemagick

:warning: CentOSとかならyumでインストールしてください

  • Gem追加

paperclipとaws-sdk, rmagickのGemを追加して

gem 'paperclip'
gem 'aws-sdk'
gem 'rmagick'

bundle install します

$ bundle install

Gemfile.lockが更新されます。

Model

  • Modelに画像保存用の設定を追加する
class Hoge < ActiveRecord::Base
  has_attached_file :image,
                    :styles => {
                        :thumb  => "90x60",
                        :medium => "180x120",
                        :large => "600x400"
                    },
                    :storage => :s3,
                    :s3_permissions => :private,
                    :s3_credentials => "#{Rails.root}/config/s3.yml",
                    :path => ":attachment/:id/:style.:extension"

  validates_attachment_content_type :image, :content_type => ["image/jpg", "image/jpeg", "image/png"]

  def authenticated_image_url(style)
    image.s3_object(style).url_for(:read, :secure => true)
  end

end
  • Migrationを作る
$ rails g migration AddAttachmentImageToHoges
  • migrationファイルの中身はこんな感じ
class AddAttachmentImageToHoges < ActiveRecord::Migration
  def change
    change_table :hoges do |t|
      t.has_attached_file :image
    end

    drop_attached_file :hoges, :image
  end
end
  • Migrate
$ rake db:migrate

これで

  • image_file_name
  • image_content_type
  • image_file_size
  • image_updated_at

の4つのカラムがDBに追加されます。

Controller, View

次にControllerとViewを作っていきます。

$ rails g controller hoges index show new edit

Controller

class HogesController < ApplicationController
  before_action :set_hoge, only: [:show, :edit]

  def index
    @hoges = Hoge.all
  end

  def new
    @hoge = Hoge.new
  end

  def show
  end

  def edit
  end

  # とりあえず、createだけ追加
  def create
    @hoge = Hoge.new(hoge_params)

    respond_to do |format|
      if @hoge.save
        format.html { redirect_to @hoge, notice: 'Hoge was successfully created.' }
        format.json { render action: 'show', status: :created, location: @hoge }
      else
        format.html { render action: 'new' }
        format.json { render json: @hoge.errors, status: :unprocessable_entity }
      end
    end
  end

  private
    def set_hoge
      @hoge = Hoge.find(params[:id])
    end

    def hoge_params
      params.require(:hoge).permit(:name, :image)
    end

end

View

  • 新規登録画面
  <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %><br>
    <%= f.label :image %>
    <%= f.file_field :image %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
  • 一覧画面
    <% @hoges.each do |hoge| %>
      <tr>
        <td><%= hoge.id %></td>
        <td><%= image_tag(hoge.authenticated_image_url(:thumb)) if hoge.image.present? %></td>
        <td><%= link_to 'show', hoge %></td>
        <td><%= link_to 'edit', edit_hoge_path(hoge) %></td>
        <td><%= link_to 'destroy', hoge, method: :delete, data: { confirm: 'are you sure?' } %></td>
      </tr>
    <% end %>
  • 部分テンプレート
  <%= @hoge.name %>
  <%= image_tag(@hoge.authenticated_image_url(:large)) if @hoge.image.present? %>

Routes

hogesへのrouteを追加します。

  resources :hoges

S3

Bucketを作る

AWSのManagementConsole上でS3のBucketを作ります。

f:id:kentana20:20141202230827j:plain

f:id:kentana20:20141202230840j:plain

接続情報

  • config/s3.yml として、S3への接続情報を記述します
bucket: 'bucket_name'
access_key_id: '12345678'
secret_access_key: '12345678901234567890'
s3_host_name: 's3-ap-northeast-1.amazonaws.com' # tokyoリージョンの場合

これで準備は完了です。

実行

Railsアプリを動かして /hoges/ へアクセスして、"new hoge"リンクを押下して画像データを追加します。

CreateしたらAWSのコンソール上でS3のBucketの中身を確認します。

f:id:kentana20:20141202230910j:plain

所感

  • paperclip便利
    • めちゃ便利です
    • S3、ImageMagickとの連携が非常に手軽に行えます
  • 静的コンテンツをWebサーバに置かなくていいのはラク
    • 静的コンテンツをWebサーバ内に置かなくていいのは非常に良いですね
    • S3ならそのままCloudFrontでCDN対応もできます
    • Webサーバをスケールアウトしても静的コンテンツはそのままで良いし、運用もラクです
  • 画像ファイルのURL生成をRails側で処理することについては...
    • 一方、画像URL生成をRailsで処理させることになるので、ここはフラグメントキャッシュを使って、キャッシュした方が良い部分かもしれません
  • CarrierWave
    • 対抗馬と目される、Carrierwaveも試してみようと思います

参考