ECのウェブ担当者のメモ

ECサイトを運営管理している、WEB担当プログラマのメモ

スポンサーリンク

rails4で画像をS3にアップロードする [carrierwave + imagemagick + fog + S3 + Dropzone]

f:id:jun9632:20160328194228p:plain

carrierwave + imagemagick + fog + S3 + Dropzone の組み合わせで、画像をアップロードして、S3に保存する方法です。

作業方法を下記していきます。

imagemagickのインストール

Macだったら、brewでimagemagick をインストールします。

$ brew install imagemagick

EC2のLinuxマシンでyumが使えれば

$ yum install imagemagick

Gemの追加

続いて、Gemを追加します。 今回追加するGemは、以下です。

  • carrierwave
  • rmagick
  • fog

carrierwave

github.com

carrierwaveがファイルのアップロード担当です。

rmagick

github.com

画像の加工を担当します。 今回は画像のリサイズのために使います。

fog

github.com

クラウド系のストレージ簡単に接続してくれるGemです。 今回はS3接続のために使用します。

Gemfile

上記のGemを追加するため、 Gemfileに以下を追加して、bundle install します。

# https://github.com/carrierwaveuploader/carrierwave
gem 'carrierwave'
# https://github.com/rmagick/rmagick
gem 'rmagick', require: 'RMagick'
# https://github.com/fog/fog
gem 'fog'

Modelの追加

今回は、商品の画像を想定しており、Item というモデルに複数の ItemImage モデルが紐づくテーブル構造です。

まず、下記を実行して、モデルのベースを作成します。

$ rails g model item
$ rails g model item_image

上記を実行すると、migrationファイルも生成されるので、それぞれの migrationファイルを以下のように編集します。 columnについては適宜変更してください。

class CreateItems < ActiveRecord::Migration
  def change
    create_table :items do |t|

      t.string :code
      t.string :name

      t.timestamps null: false
    end

    add_index :items, [:code]

  end
end
# -*- encoding : utf-8 -*-
class CreateItemImages < ActiveRecord::Migration
  def change
    create_table :item_images do |t|

      t.integer :item_id, null: false

      t.text :image, null: false
      t.string :alt
      t.integer :row_order

      t.timestamps null: false
    end
  
    add_index :item_images, [:item_id]
  
  end

end

マイグレーションファイルを編集したら、以下を実行してテーブルを作成します。

$ rake db:migrate

uploaderの作成

続いてuploaderを作成します。 下記を実行してUploaderを作成します。

$ rails g uploader item_image

app/uploaders/item_image_uploader.rbが作成されるので

下記のように編集します。 下記のサンプルでは、アップロードした画像を、必ず正方形にするようなリサイズをRMagickで行っています。

RMagickの挙動については、こちらがとてもわかり易いです。

noodles-mtb.hatenablog.com

class ItemImageUploader < CarrierWave::Uploader::Base

  include CarrierWave::RMagick

  storage :fog

  # s3のディレクトリ
  def store_dir
    "/img/up/item/#{model.id}"
  end

  version :_100x100 do
    process :resize_and_pad => [100, 100, '#fff']
  end

  version :_280x280 do
    process :resize_and_pad => [280, 280, '#fff']
  end

  version :_540x540 do
    process :resize_and_pad => [540, 540, '#fff']
  end

  version :_800x800 do
    process :resize_and_pad => [800, 800, '#fff']
  end

  # 許可する画像の拡張子
  def extension_white_list
    %w(jpg jpeg gif png)
  end


  def filename
    "img.jpg"
  end

end

デフォルトだと、filenameに"Time.now.strftime('%Y%m%d%H%M%S').jpg"みたいになっていますが、 複数versionファイルを作成して、以下のように versionでアクセスしようとすると オリジンを作った時間と_800x800を作った時間がずれることがあるため決まった定数のようなものを指定したほうがよいです。

item_image.image.url(:_800x800)

s3の作成

s3のバケット作成

サクッと作成できると思うので、今回は割愛します。ご了承ください。

s3のユーザー作成

S3用のユーザーを作成します。 S3だけの権限を与えたユーザーを作った方が良いと思います。

作り方は下記をご参考ください。

blog.cloudpack.jp

carrierwave + fogの設定

config/initializers/carrierwave.rb を作成して以下のように編集します。 S3にアップロードするための接続設定を行っていきます。

CarrierWave.configure do |config|
  config.fog_credentials = {
      :provider               => 'AWS',
      :aws_access_key_id      => ENV['S3_ACCESS_KEY_ID']
      :aws_secret_access_key  => ENV['S3_SECRET_ACCESS_KEY']
      :region                 => 'ap-northeast-1', # Tokyo
      :path_style             => true,
  }

  config.fog_public     = true # public-read
  config.fog_attributes = {'Cache-Control' => 'public, max-age=86400'}

  case Rails.env
    when 'production'
      config.fog_directory = 'xxxxx.com'
      config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/xxxxx.com'
    when 'staging'
      config.fog_directory = 'staging.xxxxx.com'
      config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/staging.xxxxx.com'
    when 'development'
      config.fog_directory = 'dev.xxxxx.com'
      config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/dev.xxxxx.com'
    when 'test'
      config.fog_directory = 'test.xxxxx.com'
      config.asset_host = 'https://s3-ap-northeast-1.amazonaws.com/test.xxxxx.com'
  end
end

S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYは、作成したS3アクセス用のユーザーのACCESS_KEYとSECRET_ACCESS_KEYを設定してください。 また、xxxxx.com には、作成したbacket名に適宜変更してください。

routes.rd

route.rdは以下のように設定しました。 今回は、:edit, :update, :destroy はありませんが、altカラムの編集と画像の削除に設定しています。

    resources :items, only: [:show]
      resources :item_images, only: [:edit, :update, :destroy] do
        collection do
          post :upload
        end
      end
    end

Modelの編集

各モデルに設定を追加していきます。

Item

# -*- encoding : utf-8 -*-
class Item < ActiveRecord::Base

  has_many :item_images

end

ItemImage

# -*- encoding : utf-8 -*-
class ItemImage < ActiveRecord::Base
  mount_uploader :image, ItemUploader

  belongs_to :item

end

mount_uploaderで先ほど作ったItemUploaderとひも付けをします。

controller

コントローラーの設定

item_image_controller.rb

  def upload
    @item = Item.find(params[:id])
    item_image = @item.item_images.build(image: params['file'])
    item_image.save
  end

View

フロント側のViewのソースはこんな感じです。

Dropzoneを使って フロントから サーバへファイルを送ります。 こちらドラッグ&ドロップで画像を追加できるので、気に入っています。

Dropzone.js

item/show.html

    = render 'items/image_upload_form'

items/_image_upload_form.html.slim

= form_tag(upload_item_item_images_path(@item),
        :id => 'item-image-dropzone', :class => 'dropzone', method: :post)

javascript:

  Dropzone.autoDiscover = false;

  Dropzone.options.itemImageDropzone = {
    paramName: "file",         // input fileの名前
    parallelUploads: 10,            // 1度に何ファイルずつアップロードするか
    acceptedFiles: 'image/*',   // 画像だけアップロードしたい場合
    maxFiles: 10,                      // 1度にアップロード出来るファイルの数
    maxFilesize: 100,                // 1つのファイルの最大サイズ(1=1M)
    dictFileTooBig: "ファイルが大きすぎます。 ({{filesize}}MiB). 最大サイズ: {{maxFilesize}}MiB.",
    dictInvalidFileType: "画像ファイル以外です。",
    dictMaxFilesExceeded: "一度にアップロード出来るのは10ファイルまでです。",
    dictDefaultMessage: 'アップロードする画像をここにドラッグ&ドロップしてください。'
  };
  var itemImageDropzone = new Dropzone("form#item-image-dropzone");

まとめ

きっと、以上でファイルアップロードができると思いす。 適当にモデル等々を変更してください。

関連記事

marketing-web.hatenablog.com

実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミング

実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミング