Edited at

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


はじめに

プログラミングを始めて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