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のインストールが必要。
$ sudo yum install ImageMagick ImageMagick-devel
Railsプロジェクトの生成
$ rails new pictures
$ cd pictures
さくっと、Railsプロジェクトを生成。
Gemfile
#(snip)
#Image
gem 'rmagick'
gem 'paperclip'
#AWS
gem 'aws-sdk'
$ bundle install --path vendor/bundler
これで、もろもろ必要なGemたちがインストールされる。
モデルの生成
$ 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
こんな感じのリレーションモデル。Pictureモデルのimage_xxxx
な4つのカラムは、PaperClipが自動で作ってくれるので、自分ではarticle_id
だけ用意してあげればいい。
画像ファイル用のカラムを作成
$ bundle exec rails g migration AddAttachmentImageToPicture
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
$ bundle exec rake db:migrate
これでマイグレーションして適用させると、Pictureモデルに4つのカラムが追加されている。これで添付画像の情報を管理する。
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
を指定しておく。これでわざわざ削除するロジックを気にすることはなくなる。
class Article < ActiveRecord::Base
has_many :pictures, :dependent => :destroy
end
Pictureモデルに画像の情報を追加
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の設定
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ですでに投稿フォームができているので、ファイル添付のフォームパーツを追記してあげる。
<%= form_for @question, :html => { multipart: true } do |f| %>
まず、画像ファイルをアップロードするので、<form>
タグのenctypeをmultipart指定してあげる。
<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を指定すると、一度に複数のファイルを選択することが可能。
表示ページ
<% 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に保存していく。実際には、保存成功失敗の成功可否を取得してあげたほうが良いと思う。
新規登録
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
を追記してあげる。
def article_params
params.require(:article).permit(:title, :description, :pictures)
end
更新
すでに登録済みの画像は全て削除して、新しくアップロードする画像に差し替えるようにする。
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を使って、様々なサムネイルサイズの画像を作って保存することが出来る。
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。
:medium<br>
<%= image_tag pic.image.url(:medium) %><br>
:square<br>
<%= image_tag pic.image.url(:square) %><br>