rails4で画像をS3にアップロードする [carrierwave + imagemagick + fog + S3 + Dropzone]
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
carrierwaveがファイルのアップロード担当です。
rmagick
画像の加工を担当します。 今回は画像のリサイズのために使います。
fog
クラウド系のストレージ簡単に接続してくれる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の挙動については、こちらがとてもわかり易いです。
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だけの権限を与えたユーザーを作った方が良いと思います。
作り方は下記をご参考ください。
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を使って フロントから サーバへファイルを送ります。 こちらドラッグ&ドロップで画像を追加できるので、気に入っています。
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");
まとめ
きっと、以上でファイルアップロードができると思いす。 適当にモデル等々を変更してください。
関連記事
実践Ruby on Rails 4 現場のプロから学ぶ本格Webプログラミング
- 作者: 黒田努
- 出版社/メーカー: インプレス
- 発売日: 2014/06/27
- メディア: Kindle版
- この商品を含むブログを見る