LoginSignup
145
140

More than 5 years have passed since last update.

Railsで、超簡単便利なPaperClipを使って、複数画像をS3にアップロード

Posted at

Railsでファイルアップロードといえば、PaperClipかCarrierWaveっていうことらしいんだけど、今回はシンプルなPaperClipにしてみる。そんでもって、1個の画像じゃなくて、複数の画像をAWSのS3にアップロードする。サムネイルも簡単に作れるし、便利ですね。

前提

  • Rails v4.1
  • Gem : PaperClip v4.2.0
  • Gem : AWS-SDK v1.54.0
  • Gem : RMagick v2.13.3

必要なパッケージ

RMagickを使うため、事前にImageMagickのインストールが必要。

command
$ sudo yum install ImageMagick ImageMagick-devel

Railsプロジェクトの生成

command
$ rails new pictures
$ cd pictures

さくっと、Railsプロジェクトを生成。

Gemfile

Gemfile
#(snip)

#Image
gem 'rmagick'
gem 'paperclip'

#AWS
gem 'aws-sdk'
command
$ bundle install --path vendor/bundler

これで、もろもろ必要なGemたちがインストールされる。

モデルの生成

command
$ bundle exec rake db:create
$ bundle exec rails g scaffold article title:string description:text
$ bundle exec rails g model picture article_id:integer
$ bundle exec rake db:migrate

スクリーンショット 2014-10-12 14.44.20.png

こんな感じのリレーションモデル。Pictureモデルのimage_xxxxな4つのカラムは、PaperClipが自動で作ってくれるので、自分ではarticle_idだけ用意してあげればいい。

画像ファイル用のカラムを作成

command
$ bundle exec rails g migration AddAttachmentImageToPicture
db/migrate/0000_add_attachment_image_to_picture.rb
class AddAttachmentImageToPicture < ActiveRecord::Migration
  def self.up
    change_table :pictures do |t|
      t.attachment :image
    end
  end

  def self.down
    drop_attached_file :pictures, :image
  end
end
command
$ bundle exec rake db:migrate

これでマイグレーションして適用させると、Pictureモデルに4つのカラムが追加されている。これで添付画像の情報を管理する。

pictures
CREATE TABLE "pictures" (
    "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 
    "article_id" integer, 
    "created_at" datetime, 
    "updated_at" datetime, 

    -- 以下4つのカラムが自動追加されている
    "image_file_name" varchar(255), 
    "image_content_type" varchar(255), 
    "image_file_size" integer, 
    "image_updated_at" datetime
);

Articleモデルにアソシエーション情報を追加

Articleに複数の画像が紐づくので、has_manyでPicutreモデルを指定。そんで、Articleを削除する際に、一緒に画像ファイルも消したいので、:dependent:destroyを指定しておく。これでわざわざ削除するロジックを気にすることはなくなる。

app/models/article.rb
class Article < ActiveRecord::Base
  has_many :pictures, :dependent => :destroy
end

Pictureモデルに画像の情報を追加

app/models/picture.rb
class Picture < ActiveRecord::Base
  belongs_to :article

  has_attached_file :image,
                    :storage => :s3,
                    :s3_permissions => :public,
                    :s3_credentials => "#{Rails.root}/config/s3.yml",
                    :path => ":attachment/:id/:style.:extension"

  do_not_validate_attachment_file_type :image
end

AWS S3の設定

config/s3.yml
bucket: 'bucket-name'
access_key_id: 'ACCESS_KEY'
secret_access_key: 'SECRET_ACCESS_KEY'
s3_host_name: 's3-ap-northeast-1.amazonaws.com'

s3_host_nameは、リージョンによって違う。

リージョン名 ホスト名
Asia Pacific (Tokyo) s3-ap-northeast-1.amazonaws.com
Asia Pacific (Singapore) s3-ap-southeast-1.amazonaws.com
US Standard s3.amazonaws.com
US-West (Northern California) s3-us-west-1.amazonaws.com
EU (Ireland) s3-eu-west-1.amazonaws.com

Viewの設定

投稿フォーム

scaffoldですでに投稿フォームができているので、ファイル添付のフォームパーツを追記してあげる。

app/views/articles/_form.html.erb
<%= form_for @question, :html => { multipart: true } do |f| %>

まず、画像ファイルをアップロードするので、<form>タグのenctypeをmultipart指定してあげる。

app/views/articles/_form.html.erb
<ul>
    <li>ファイル1:<%= file_field_tag "images[]", type: :file %></li>
    <li>ファイル2:<%= file_field_tag "images[]", type: :file %></li>
</ul>

<!-- ↓↑どちらか -->

<ul>
    <li>ファイル複数選択:<%= file_field_tag "images[]", type: :file, multiple: true %></li>
</ul>

続いて、適当な場所に<input type="file">なフォームパーツを設置。mutipleを指定すると、一度に複数のファイルを選択することが可能。

表示ページ

app/views/articles/show.html.erb
<% unless @article.pictures.empty? %>
    <p>画像です</p>
    <ul>
      <% @article.pictures.each do |pic| %>
          <li id="picture_<%= pic.id %>">
            <%= image_tag pic.image.url %>
          </li>
      <% end %>
    </ul>
<% end %>

Controllerの設定

ここでparams[:images]にアップロードファイルの情報が入ってくるので、1つずつhas_manyなpicturesに保存していく。実際には、保存成功失敗の成功可否を取得してあげたほうが良いと思う。

新規登録

app/controllers/articles_controller.rb
  def create
    @article = Article.new(article_params)

    respond_to do |format|
      if @article.save

        # 画像のアップロード対応
        if params[:images]
          params[:images].each { |image|
            @article.pictures.create(image: image)
          }
        end

        format.html { redirect_to @article, notice: 'Article was successfully created.' }
        format.json { render :show, status: :created, location: @article }
      else
        format.html { render :new }
        format.json { render json: @article.errors, status: :unprocessable_entity }
      end
    end
  end

また、受け取るパラメータとして、:picturesを追記してあげる。

app/controllers/articles_controller.rb
    def article_params
      params.require(:article).permit(:title, :description, :pictures)
    end

更新

すでに登録済みの画像は全て削除して、新しくアップロードする画像に差し替えるようにする。

app/controllers/articles_controller.rb
  def update
    respond_to do |format|
      if @article.update(article_params)

        # 画像のアップロード対応
        if params[:images]

          # 前回登録してある画像は全て削除
          Article.find(params[:id]).pictures.each do |image|
            image.destroy
          end

          # 代わりに今回アップする画像に差し替え
          params[:images].each { |image|
            @article.pictures.create(image: image)
          }
        end

        format.html { redirect_to @article, notice: 'Article was successfully updated.' }
        format.json { render :show, status: :ok, location: @article }
      else
        format.html { render :edit }
        format.json { render json: @article.errors, status: :unprocessable_entity }
      end
    end
  end

ファイルアップロードの完成

ここまでの設定とコードで、とりあえず複数のファイルを新規アップロードすることが可能なはず。

サムネイルサイズの指定

PaperclipはImageMagickを使って、様々なサムネイルサイズの画像を作って保存することが出来る。

app/models/picture.rb
  has_attached_file :image,
                    :storage => :s3,
                    :s3_permissions => :public,
                    :s3_credentials => "#{Rails.root}/config/s3.yml",
                    :path => ":attachment/:id/:style.:extension",
                    :styles => {
                      :original => '1980x1680>',
                      :square => '100x100#',
                      :medium => '300x240>'
                    }

:stylesで、複数の画像サイズを指定可能。

指定方法 効果
100x100> アスペクト比を維持して、長辺を100pxに合わせ縮小する
100x100^ アスペクト比を維持して、短辺を100pxに合わせ縮小する
100x100# 短辺を100pxに合わせ縮小し、中央でトリミングする
100x100! アスペクト比を無視して、縦横100pxに合わせ縮小する

よく使うのは>#の2つかな。

Viewファイルでのサムネイル画像参照

サムネイルの種類を名前で指定してあげればOK。

app/views/articles/show.html.erb
:medium<br>
<%= image_tag pic.image.url(:medium) %><br>

:square<br>
<%= image_tag pic.image.url(:square) %><br>
145
140
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
145
140