読者です 読者をやめる 読者になる 読者になる

Rails Webook

自社のECを開発している会社で働いています。Rails情報やサービスを成長させる方法を書いていきます

RailsでPaperclipを使ってファイルをアップロードする

ファイルアップロード Rails初級

Paperclipはは有名なファイルアップロード用のgemです。他にはCarrierWaveというものもあります。
この2つがRailsでのファイルアップロードgemとしてトップ2を争っています。

これらの違いは、

  • Paperclipは機能がシンプルで使いやすい
  • CarrierWaveは機能がいろいろあり、応用が聞きやすい

です。

Paperclipではアップロードしたファイルはサーバー上に配置されます。

実装結果

追加/編集画面でファイルをアップロードします。
f:id:nipe880324:20140716033100p:plain:w480

すると。。。詳細画面にアップロードした画面が表示されます。
f:id:nipe880324:20140716033033p:plain:w480

動作確認

  • Mac OS X 10.9
  • Ruby 2.0.0
  • Rails 4.1.4
  • Paperclip 4.2.0

目次

  1. Railsプロジェクトの作成
  2. PaperclipのRailsへの導入
  3. Paperclipのアップロードファイルの保存先とValidation
  4. Paperclipへコンソール($ rails console)から画像ファイルの追加
  5. PaperclipでCSVファイルをアップロードする

1. Railsプロジェクトの作成

まず Railsプロジェクトを作成します。

$ rails new paperclip_test

そして Scaffoldで簡単なPictureクラスを作ります。

$ cd paperclip_test
$ rails g scaffold Picture name:string

では DBマイグレートをして、ローカル環境で動かしてみましょう。

$ rake db:migrate
$ rails s

以下のような画面ができるはずです。
f:id:nipe880324:20140716033119p:plain:w480


2. PaperclipのRailsへの導入

では、Paperclipをプロジェクトに導入します。
と、その前に ImageMagick という画像変換ツールをインストールが必要です。
Macの場合は、以下のコマンドで実施可能です。その他の環境では、上記のリンクからインストール方法を確認して、入れて下さい。

$ brew install imagemagick

imagemagickがインストールされると、convertコマンドが使えるようになりますので、確認で次のコマンドでパスが表示されるか確認しましょう。

$ which convert
/usr/local/bin/convert

さて、いよいよpaperclipを追加します。

# Gemfile
  ...
  gem "paperclip"

gemをインストールします。

$ bundle install

つぎに Pictureテーブルに「ファイルの保存用のphotoカラムを追加するマイグレートファイル」を以下のコマンドで作成します。

$ rails g paperclip picture photo
      create  db/migrate/20140715171251_add_attachment_photo_to_pictures.rb

中身をいちおう確認しましょう。
attachmentは、Paperclipが定義しているファイルで、ファイル情報を保持するための複数のカラムを追加します。

$ cat db/migrate/20140715171251_add_attachment_photo_to_pictures.rb
class AddAttachmentPhotoToPictures < ActiveRecord::Migration
  def self.up
    change_table :pictures do |t|
      t.attachment :photo
    end
  end

  def self.down
    remove_attachment :pictures, :photo
  end
end

マイグレーションファイルの中身は編集しないで、マイグレートさせます。

$ rake db:migrate
== 20140715171251 AddAttachmentPhotoToPictures: migrating =====================
-- change_table(:pictures)
   -> 0.0015s
== 20140715171251 AddAttachmentPhotoToPictures: migrated (0.0016s) ============

次にPicture.dbモデルにphoto用のカラムを記載します。

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

  # photoをattachファイルとする。stylesで画像サイズを定義できる
  has_attached_file :photo, styles: { medium: "300x300>", thumb: "100x100>" }

  # ファイルの拡張子を指定(これがないとエラーが発生する)
  validates_attachment :photo, content_type: { content_type: ["image/jpg", "image/jpeg", "image/png", "image/gif"] }
end

次に、ビューに画像ファイルアップロード用のinput要素を追加します。

ファイルなどをサーバに送信する場合、HTMLの仕様としてmultipartという属性が必要なのですが、form_forメソッド時のときは自動で追加されるため今回は追加が必要ありません。form_tagメソッドの時は自分で記載する必要があります。

# app/views/pictures/_form.html.erb
  ...
  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.file_field :photo %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

アップロードした画像を表示するための表示領域を、詳細画面に追加します。

# app/views/pictures/show.html.erb

...
<p>
  <strong>Name:</strong>
  <%= @picture.name %>
</p>

<!-- 追加箇所 開始-->
<p>
   <strong>Picture:</strong>
   <!--- Modelのstyleで指定した :midium など渡すことでサイズを指定できる -->
   <%= image_tag @picture.photo.url(:medium) %>
</p>
<!-- 追加箇所 終了-->
...

最後に、StrongParameterにinput属性のphotoを追加しましょう。

# app/controllers/pictures_controller.rb
    ...

    # Never trust parameters from the scary internet, only allow the white list through.
    def picture_params
      params.require(:picture).permit(:name, :photo)
    end
end

では、実際にアップロードした画像を確認してみましょう。画面に表示されていますね。
アップロードしたファイルはpublic/system配下に配置されています。
f:id:nipe880324:20140716033033p:plain:w480




3. Paperclipのアップロードファイルの保存先とValidation

ファイルアップロードの保存先はurlとpathで指定することができます。
また、Validationには次のような項目があります。

# app/models/picture.rb

class Picture < ActiveRecord::Base
    has_attached_file :photo, 
  	:styles => { medium: "300x300>", thumb: "100x100>" }, # 画像サイズを指定
	:url  => "/assets/arts/:id/:style/:basename.:extension", # 画像保存先のURL先
	:path => "#{Rails.root}/public/assets/arts/:id/:style/:basename.:extension" # サーバ上の画像保存先パス

    ## Validation
    validates_attachment :photo,
      presence: true,  # ファイルの存在チェック
      less_than: 5.megabytes, # ファイルサイズのチェック
end

上記の設定の場合、実際に次の箇所にアップロードされたファイルは保存されます。
f:id:nipe880324:20140716033017p:plain:w480




4. Paperclipへコンソール(rails console)から画像ファイルの追加

テストなどでファイルアップロードをRailsのコンソールrails consoleから実施したい場合があると思います。
その方法は次のようにできます。

$ rails c

Loading development environment (Rails 4.1.4)
# カラムにファイルを追加
> Picture.new(photo: File.new("app/assets/images/avatar/Aussie_Bear_toy_koala.jpg", "r"))

# 保存
> Picture.save

# create や update_attributeメソッドも同様に更新可能


5. PaperclipでCSVファイルをアップロードする

CSVをPaperclipでアップロードし、次のように配置先のリンクを表示し、ダウンロードできるようにします。
f:id:nipe880324:20150114224014j:plain:w480


Paperclipでcsvカラムを追加します。

bin/rails g paperclip Job csv   
bin/rake db:migrate


モデルにCSV用のバリデーションを追加します。

# app/models/job.rb
class Job < ActiveRecord::Base
  has_attached_file :csv
  validates_attachment_presence :csv
  validates_attachment_content_type :csv, :content_type => ['text/csv','text/comma-separated-values','application/csv','text/plain']
end


StrongParametersにcsvを追加します。

def job_params
  params.require(:job).permit(:name, :priority, :csv)
end


フォームにファイルフィールドを追加します。

<%= form_for(@job) do |f| %>

  ...

    <%= f.label :csv %><br>
    <%= f.file_field :csv %>

    <%= f.submit %>
<% end %>


表示時にCSVファイル名を表示します。

<%= link_to job.csv_file_name, job.csv.url %>

以上です。

Todo

  • 本格的なバリデーション(ファイルサイズ、コンテンツタイプを偽ったファイルのアップロードなどのセキュリティ)
  • S3へのアップロード