LoginSignup
24
29

More than 5 years have passed since last update.

【Rails】Shrineの使い方【プログラミング学習156日目】

Last updated at Posted at 2019-01-29

はじめに

プログラミングを始めて5ヶ月が経過しました。時が経つのは本当に早いですね。。。

先日1つ目のポートフォリオが完成し、2つ目のポートフォリオ制作に取り掛かっています。

画像をアップロード用のgemを調べていたところ、Shrineというイケてそうなgemを見つけました。

1つ目ではRailsチュートリアルに従って、CarrierWaveを用いていたのですが、2作品目では違うものを扱ってみたい!という考えから、今回はこのShrineを用いてアップロード機能を実装することにしました。

shrine gemとは?

  • Shrine (公式)
  • shrine (GitHub)
  • ファイル(主に画像)をアップロードするためのツール
  • 特徴としては、CarrierWaveよりもシンプルであり、必要に応じてプラグインを追加することでリサイズやAWS S3へのダイレクトアップロードなどの機能を実装可能
  • その他には、英語情報も含めて情報が多いことや、開発が止まってない(頻繁にcommitやプルリクエストされている)点が魅力的

開発環境

  • Ruby 2.5.1
  • Rails 5.2.2
  • Devise 4.5.0
  • Shrine 2.14.0

前提

  • 認証系にはDeviseを用いており、current_userメソッドなどが使用できる状態であるとします。
  • ユーザープロフィール画像をアップロードしたい、という状況を考えていきます。

shrine gemの使い方

主に以下3つの兄弟記事を参考にShrineの導入を進めました。
- Shrineを使って画像をアップロードする
- Shrineでアップロードする際に画像を加工する
- ShrineでS3に画像をアップロードする

今回は、下記3点について説明していきます。

  1. Shrineの導入、/public/uploadsフォルダへの格納機能の実装
  2. 画像リサイズ機能の導入・バリデーション設定
  3. AWS S3へのダイレクトアップロード機能の導入・設定

1. Shrineの導入

gemの追加

まず、shrine gemを追加し、$ bundle installを実行します。

Gemfile
gem 'shrine'

設定ファイルの作成

次に、$ touch config/initializers/shrine.rbにてShrineの設定ファイルを作成し、中身を記述していきます。

これで、画像データを送信した時に/public/uploads/cacheフォルダ(一時保存用)および/public/uploads/storeフォルダ中(永久保存用)に画像が入るようになります。

また、cached_attachment_dataプラグインを追加することで、フォーム中で= f.hidden_field :image, value: current_user.cached_image_dataのように、キャッシュデータを送信できるようになります。

config/initializers/shrine.rb
require "shrine"
require "shrine/storage/file_system"

# アップロードするディレクトリの指定
Shrine.storages = {
    cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
    store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store")}

# 使用するプラグインの宣言
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data

Userモデルにimage_data属性を追加

続いて、$ rails g migration add_column_to_users image_data:textでUserモデルにimage_data属性を追加します。

ここで、image_data属性のデータ型はtextである点に気をつけて下さい。
stringにするといざデータを送信した時に文字数制限に引っ掛かりエラーが出ました。)

その後、忘れずに$ rails db:migrateを実行します。

ImageUploaderモデルの作成

app/uploaders/image_uploader.rbファイルを作成し、中身を記述します。
基本部分の導入ではこれだけで問題ありません。

app/uploaders/image_uploader.rb
class ImageUploader < Shrine
end

Userモデルの編集

Userモデルに実装したアップローダについて追記します。
ここで、[:image_data]ではなく、[:image]とすることに注意です。

app/models/user.rb
class User < ApplicationRecord
  include ImageUploader[:image]  # ここを追記、[:image_data]としないこと
  
  
  
end

Strong Parameterの編集

Strong Parameterに:imageを追加して、画像データが保存できるように設定します。
ここでも、[:image_data]ではなく、[:image]とすることに注意です。

また、今回はDeviseを使用しているので、app/controllers/application_controller.rb中のDevise用のStrong Parameter中に記述していますが、時と場合によってコントローラは変えて下さい。

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_in, keys: [:name, :email, :password, :password_confirmation])
    devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :email])
      # 今回は以下に追記、[:image_data]としないこと
    devise_parameter_sanitizer.permit(:account_update, keys: [:name, :email, :password, :password_confirmation, :image])
  end
end

ビューの実装

対象のビューを作成し、実装していきます。
今回はDeviseで自動生成されたユーザー編集ページ(app/views/devise/registrations/edit.html.slim)に記述しています。

f.labelの部分だけimage_dataとなることに注意して下さい。

また、f.hidden_fieldにてキャッシュデータを送信するようにしています。
.cached_image_dataは前述のcached_attachment_dataプラグインがなければエラーが出ます。)

app/views/devise/registrations/edit.html.slim
= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {method: :put}) do |f|
  
  
  
  = f.label :image_data, "プロフィール画像"
  = f.hidden_field :image, value: current_user.cached_image_data
  = f.file_field :image
  
  
  
  = f.submit "ユーザー情報を更新"

以上で画像をアップロード機能は実装できました。
引き続き、画像のリサイズとAWS S3へのダイレクトアップロードについて説明していきます。

2. 画像リサイズ・バリデーション設定

画像のリサイズ・バリデーションについては、shrine (GitHub)中に全て書いているため、を見ながら進めるとスムーズです。

ImageMagickのインストール

>_ターミナル
$ brew install imagemagick

image_processing gemの追加

以下のgemを追加して$ bundle installを実行します。

Gemfile
gem 'image_processing'
gem 'mini_magick'

ImageUploaderモデルの編集

先程作成したapp/uploaders/image_uploader.rbの中身を記述していきます。

app/uploaders/image_uploader.rb
require "image_processing/mini_magick"

class ImageUploader < Shrine
  plugin :processing # allows hooking into promoting
  plugin :versions   # enable Shrine to handle a hash of files
  plugin :delete_raw # delete processed files after uploading

  process(:store) do |io, context|
    versions = { original: io } # retain original

    io.download do |original|
      pipeline = ImageProcessing::MiniMagick.source(original)

      versions[:large]  = pipeline.resize_to_limit!(800, 800)
      versions[:medium] = pipeline.resize_to_limit!(500, 500)
      versions[:small]  = pipeline.resize_to_limit!(300, 300)
    end

    versions # return the hash of processed files
  end
end

これによって、以下のようなハッシュが生成されるようになります。

そして、この設定をすることでphoto.image[:medium].urlのような形でハッシュに格納された画像データをリサイズしてから取り出すことが可能になります。

保存される画像データの構造
photo.image_data #=>
# '{
#   "original": {"id":"9sd84.jpg", "storage":"store", "metadata":{...}},
#   "large": {"id":"lg043.jpg", "storage":"store", "metadata":{...}},
#   "medium": {"id":"kd9fk.jpg", "storage":"store", "metadata":{...}},
#   "small": {"id":"932fl.jpg", "storage":"store", "metadata":{...}}
# }'

photo.image #=>
# {
#   :original => #<Shrine::UploadedFile @data={"id"=>"9sd84.jpg", ...}>,
#   :large    => #<Shrine::UploadedFile @data={"id"=>"lg043.jpg", ...}>,
#   :medium   => #<Shrine::UploadedFile @data={"id"=>"kd9fk.jpg", ...}>,
#   :small    => #<Shrine::UploadedFile @data={"id"=>"932fl.jpg", ...}>,
# }

photo.image[:medium]           #=> #<Shrine::UploadedFile>
photo.image[:medium].url       #=> "/uploads/store/lg043.jpg"
photo.image[:medium].size      #=> 5825949
photo.image[:medium].mime_type #=> "image/jpeg"

バリデーション

前述のapp/uploaders/image_uploader.rbにバリデーションの項目を追記することでバリデーションが設定されます。
途中にあるplugin :validation_helpersを入れ忘れないように注意して下さい。

app/uploaders/image_uploader.rb
require 'image_processing/mini_magick'

class ImageUploader < Shrine
  plugin :processing
  plugin :versions
  plugin :delete_raw
  plugin :validation_helpers  # ここを追記

  process(:store) do |io, context|
    versions = {original: io}

    io.download do |original|
      pipeline = ImageProcessing::MiniMagick.source(original)

      versions[:large]  = pipeline.resize_to_limit!(800, 800)
      versions[:medium] = pipeline.resize_to_limit!(500, 500)
      versions[:small]  = pipeline.resize_to_limit!(300, 300)
    end

    versions
  end

  # 以下を追記
  Attacher.validate do
    validate_max_size 5 * 1024 * 1024, message: '5MBを超える画像はアップロードできません。'
    validate_mime_type_inclusion %w(image/jpeg image/png)
  end
end

以上で画像のリサイズとバリデーションの実装は完了です。

3. AWS S3へのダイレクトアップロード

AWS S3バケットの作成

最初に、AWS側でバケット作成などをする必要がありますが、ここでは参考にしたURLだけ載せて割愛します。
(S3バケットの作成方法についての参考URL)
- 【AWS】S3のバケットを作ってみる

CORSの設定

CORSについては以下の通りです。

CORSはCross-Origin Resource Sharingの略で「特定のドメインにロードされたクライアントウェブアプリケーションが異なるドメイン内のリソースと通信する方法を定義します」
ShrineでS3に画像をアップロードする

上記で作成したS3バケットにおいて、「アクセス制限」→「CORSの設定」→「CORS構成エディター」の順でクリックしていき下記の通り入力して「保存」をクリックします。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>https://your.domain.com</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Gemの追加

以下のgemを追加して$ bundle installを実行します。

Gemfile
gem "aws-sdk-s3"
group :development, :test do
  gem 'dotenv-rails'
end

.envにS3のアクセスキーなどの情報を記述

アクセスキー等は流出するとAWSを不正に悪用されてしまう危険があるので、晒さないようにします。

$ touch .envでアプリケーションのルートディレクトリに.envファイルを追加してそのファイルに以下のように記述することで一安心です。

なお、この時に、.gitignore.envを追加しておかないとプッシュした時にパスワード等が結局晒されることになるので注意が必要です。

.env
S3_BUCKET = "..."
S3_REGION = "..."
S3_ACCESS_KEY_ID = "..."
S3_SECRET_ACCESS_KEY = "..."

shrine.rbの編集

あとは設定ファイルをS3にダイレクトアップロード仕様に編集すれば完了です。

require 'shrine/storage/s3'という箇所とif Rails.env.production?〜の部分を追記していきます。

config/initializers/shrine.rb
require "shrine"
require 'shrine/storage/file_system'
require 'shrine/storage/s3'  # ここを追記

# アップロードするディレクトリの指定
# ここを追記
if Rails.env.production?
  s3_options = {
    access_key_id:     ENV['S3_ACCESS_KEY_ID'],
    secret_access_key: ENV['S3_SECRET_ACCESS_KEY'],
    region:            ENV['S3_REGION'],
    bucket:            ENV['S3_BUCKET']}

  Shrine.storages = {
    cache: Shrine::Storage::S3.new(prefix: 'cache', **s3_options),
    store: Shrine::Storage::S3.new(prefix: 'store', **s3_options)}
else
  # この部分は元々記述していた
  Shrine.storages = {
      cache: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/cache'),
      store: Shrine::Storage::FileSystem.new('public', prefix: 'uploads/store')}
end

# 使用するプラグインの宣言
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data

Railsサーバーの再起動

最後にRailsサーバーを再起動させてあげれば完了です!お疲れ様でした!

これでS3の方に画像データが保存されるようになっているはずです。

まとめ

  • Shrineの使い方をまとめた。
  • ShrineはCarrierWaveよりもシンプルであり、必要に応じてプラグイン追加によって機能の拡張が可能である。
  • Railsにて画像のリサイズやAWS S3へのダイレクトアップロードができるようにする設定を紹介した。

参考URL

24
29
0

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
24
29