Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

RailsとShrineを使用したファイルのアップロード

いくつか例を挙げると、CarrierWave、Paperclip、Dragonflyなどのファイルアップロードの宝石がたくさんあります。それらはすべて固有のものを持っており、おそらくあなたはすでにこれらの宝石の少なくとも1つを使用しています。

しかし、今日は、JankoMarohnićによって作成されたShrineと呼ばれる比較的新しいが非常にクールなソリューションを紹介したいと思います。他のいくつかの同様の宝石とは対照的に、それはモジュール式のアプローチを持っています。つまり、すべての機能がモジュール(またはプラグイン)としてパックされています。 神社の用語で)。検証をサポートしたいですか?プラグインを追加します。ファイル処理をしたいですか?プラグインを追加してください!どのモデルでどの機能を利用できるかを簡単に制御できるので、このアプローチが本当に気に入っています。

この記事では、次の方法を紹介します。

  • ShrineをRailsアプリケーションに統合する
  • 構成(グローバルおよびアップローダーごと)
  • ファイルをアップロードする機能を追加する
  • ファイルを処理する
  • 検証ルールを追加
  • 追加のメタデータを保存し、AmazonS3でファイルクラウドストレージを採用する

この記事のソースコードはGitHubで入手できます。

動作するデモはここにあります。

神社の統合

まず、デフォルトのテストスイートを使用せずに新しいRailsアプリケーションを作成します。

rails new FileGuru -T

このデモではRails5を使用しますが、ほとんどの概念はバージョン3と4にも当てはまります。

神社の宝石をGemfileにドロップします:

gem "shrine"

次に実行します:

bundle install

次に、 Photoと呼ぶモデルが必要になります。 。 Shrineは、ファイル関連のすべての情報を、 _dataで終わる特別なテキスト列に保存します。 サフィックス。対応する移行を作成して適用します:

rails g model Photo title:string image_data:text
rails db:migrate

Railsの古いバージョンの場合、後者のコマンドは次のようになります。

rake db:migrate

神社の設定オプションは、グローバルとモデルごとの両方で設定できます。もちろん、グローバル設定は初期化ファイル内で行われます。そこで、必要なファイルとプラグインを接続します。 。プラグインはShrineで使用され、機能の一部を個別のモジュールに抽出して、使用可能なすべての機能を完全に制御できるようにします。たとえば、検証、画像処理、添付ファイルのキャッシュなどのプラグインがあります。

今のところ、2つのプラグインを追加しましょう。1つはActiveRecordをサポートするためのもので、もう1つはロギングを設定するためのものです。それらはグローバルに含まれる予定です。また、ファイルシステムストレージを設定します:

config / initializers / shrine.rb

require "shrine"
require "shrine/storage/file_system"

Shrine.plugin :activerecord
Shrine.plugin :logging, logger: Rails.logger

Shrine.storages = {
  cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
  store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
}

Loggerは、ファイルの処理に費やされた時間を示すデバッグ情報をコンソール内に出力するだけです。これは便利です。

2015-10-09T20:06:06.676Z #25602: STORE[cache] ImageUploader[:avatar] User[29543] 1 file (0.1s)
2015-10-09T20:06:06.854Z #25602: PROCESS[store]: ImageUploader[:avatar] User[29543] 1-3 files (0.22s)
2015-10-09T20:06:07.133Z #25602: DELETE[destroyed]: ImageUploader[:avatar] User[29543] 3 files (0.07s)

アップロードされたすべてのファイルは、 public / uploads内に保存されます ディレクトリ。 Gitでこれらのファイルを追跡したくないので、このフォルダーを除外します:

.gitignore

public/uploads

次に、モデル固有の設定をホストする特別な「アップローダー」クラスを作成します。今のところ、このクラスは空になります:

models / image_uploader.rb

class ImageUploader < Shrine
end

最後に、このクラスを Photoに含めます モデル:

models / photo.rb

include ImageUploader[:image]

[:image] フォームを作成するときに使用される仮想属性を追加します。上記の行は次のように書き直すことができます:

  include ImageUploader.attachment(:image)  
  # or
  include ImageUploader::Attachment.new(:image) 

良い!これで、モデルに神社の機能が追加され、次のステップに進むことができます。

コントローラー、ビュー、ルート

このデモでは、写真を管理するために必要なコントローラーは1つだけです。 index ページがルートとして機能します:

pages_controller.rb

class PhotosController < ApplicationController
  def index
    @photos = Photo.all
  end
end

ビュー:

views / photos / index.html.erb

<h1>Photos</h1>

<%= link_to 'Add Photo', new_photo_path %>

<%= render @photos %>

@photosをレンダリングするには 配列、パーシャルが必要です:

views / photos / _photo.html.erb

<div>
  <% if photo.image_data? %>
    <%= image_tag photo.image_url %>
  <% end %>
  <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p>
</div>

image_data? レコードに画像があるかどうかをチェックするActiveRecordによって提示されるメソッドです。

image_url 元の画像へのパスを返すだけのShrineメソッドです。もちろん、代わりに小さなサムネイルを表示する方がはるかに優れていますが、後で処理します。

必要なルートをすべて追加します:

config / routers.rb

  resources :photos, only: [:new, :create, :index, :edit, :update]

  root 'photos#index'

これで完了です。基礎が完成し、興味深い部分に進むことができます!

ファイルのアップロード

このセクションでは、実際にファイルをアップロードする機能を追加する方法を説明します。コントローラのアクションは非常に簡単です:

photos_controller.rb

def new
    @photo = Photo.new
end

def create
    @photo = Photo.new(photo_params)
    if @photo.save
        flash[:success] = 'Photo added!'
        redirect_to photos_path
    else
        render 'new'
    end
end

唯一の落とし穴は、強力なパラメータの場合、 imageを許可する必要があるということです。 image_data ではなく、仮想属性 。

photos_controller.rb

private

def photo_params
    params.require(:photo).permit(:title, :image)
end

newを作成します ビュー:

views / photos / new.html.erb

<h1>Add photo</h1>

<%= render 'form' %>

フォームの部分も簡単です:

views / photos / _form.html.erb

<%= form_for @photo do |f| %>
  <%= render "shared/errors", object: @photo %>

  <%= f.label :title %>
  <%= f.text_field :title %>

  <%= f.label :image %>
  <%= f.file_field :image %>

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

繰り返しになりますが、 imageを使用していることに注意してください image_dataではなく属性 。

最後に、エラーを表示するために別のパーシャルを追加します:

views / shared / _errors.html.erb

<% if object.errors.any? %>
  <h3>The following errors were found:</h3>

  <ul>
    <% object.errors.full_messages.each do |message| %>
      <li><%= message %></li>
    <% end %>
  </ul>
<% end %>

これでほぼすべてです。今すぐ画像のアップロードを開始できます。

検証

もちろん、デモアプリを完成させるには、さらに多くの作業を行う必要があります。主な問題は、ユーザーがあらゆる種類のファイルをあらゆるサイズでアップロードする可能性があることです。これは特に優れているわけではありません。したがって、検証をサポートする別のプラグインを追加します。

config / inititalizers / shrine.rb

Shrine.plugin :validation_helpers

ImageUploaderの検証ロジックを設定します :

models / image_uploader.rb

Attacher.validate do
    validate_max_size 1.megabyte, message: "is too large (max is 1 MB)"
    validate_mime_type_inclusion ['image/jpg', 'image/jpeg', 'image/png']
end

1MB未満のJPGおよびPNG画像のみのアップロードを許可しています。必要に応じて、これらのルールを微調整してください。

MIMEタイプ

注意すべきもう1つの重要な点は、デフォルトでは、ShrineはContent-TypeHTTPヘッダーを使用してファイルのMIMEタイプを決定することです。このヘッダーはブラウザによって渡され、ファイルの拡張子のみに基づいて設定されますが、これは必ずしも望ましいとは限りません。

ファイルの内容に基づいてMIMEタイプを決定する場合は、determine_mime_typeというプラグインを使用します。他のモデルはこの機能を必要としない可能性があるため、アップローダークラスに含めます:

models / image_uploader.rb

plugin :determine_mime_type

このプラグインは、デフォルトでLinuxのファイルユーティリティを使用します。

添付画像のキャッシュ

現在、ユーザーが誤ったデータを含むフォームを送信すると、フォームは再び表示され、上記のエラーが表示されます。ただし、問題は、添付された画像が失われ、ユーザーがもう一度選択する必要があることです。これは、cached_attachment_dataと呼ばれるさらに別のプラグインを使用して非常に簡単に修正できます:

models / image_uploader.rb

plugin :cached_attachment_data

次に、フォームに非表示のフィールドを追加するだけです。

views / photos / _form.html.erb

<%= f.hidden_field :image, value: @photo.cached_image_data %>
<%= f.label :image %>
<%= f.file_field :image %>

写真の編集

これで画像をアップロードできますが、編集する方法がないので、すぐに修正しましょう。対応するコントローラーのアクションはやや些細なものです:

photos_controller.rb

def edit
    @photo = Photo.find(params[:id])
end

def update
    @photo = Photo.find(params[:id])
    if @photo.update_attributes(photo_params)
      flash[:success] = 'Photo edited!'
      redirect_to photos_path
    else
      render 'edit'
    end
end

同じ_form 部分的に利用されます:

views / photos / edit.html.erb

<h1>Edit Photo</h1>

<%= render 'form' %>

いいですが、十分ではありません。ユーザーはアップロードされた画像を削除できません。これを可能にするために、別のプラグインが必要になります-何を推測しますか-

models / image_uploader.rb

plugin :remove_attachment

:remove_imageという仮想属性を使用します 、コントローラー内で許可します:

photos_controller.rb

def photo_params
    params.require(:photo).permit(:title, :image, :remove_image)
end

レコードに添付ファイルがある場合は、チェックボックスを表示して画像を削除します。

views / photos / _form.html.erb

<% if @photo.image_data? %>
    Remove attachment: <%= f.check_box :remove_image %>
<% end %>

サムネイル画像の生成

現在、元の画像を表示していますが、これはプレビューに最適な方法ではありません。写真が大きく、スペースを占有しすぎる可能性があります。もちろん、CSSの widthを使用することもできます。 およびheight 属性ですが、それも悪い考えです。ご覧のとおり、スタイルを使用して画像を小さく設定した場合でも、ユーザーは元のファイルをダウンロードする必要があり、かなり大きくなる可能性があります。

したがって、最初のアップロード時にサーバー側で小さなプレビュー画像を生成することをお勧めします。これには、2つのプラグインと2つの追加のgemが含まれます。まず、宝石をドロップします:

gem "image_processing"
gem "mini_magick", ">= 4.3.5"

Image_processingは、神社の作者によって作成された特別な宝石です。画像を操作するための高レベルのヘルパーメソッドをいくつか紹介します。このgemは、ImageMagickのRubyラッパーであるmini_magickに依存しています。ご想像のとおり、このデモを実行するには、システムにImageMagickが必要です。

これらの新しいgemをインストールします:

bundle install

次に、プラグインとその依存関係を含めます。

models / image_uploader.rb

require "image_processing/mini_magick"

class ImageUploader < Shrine
    include ImageProcessing::MiniMagick
    plugin :processing
    plugin :versions
    # other code...
end

処理は、画像を操作できるようにするプラグインです(たとえば、画像を縮小、回転、別の形式に変換するなど)。次に、バージョンを使用すると、さまざまなバリエーションのイメージを作成できます。このデモでは、「original」と「thumb」( 300x300 にサイズ変更)の2つのバージョンが保存されます。 。

画像を処理してその2つのバージョンを保存するコードは次のとおりです。

models / image_uploader.rb

class ImageUploader < Shrine
    process(:store) do |io, context|
        { original: io, thumb: resize_to_limit!(io.download, 300, 300) }
    end
end

resize_to_limit! image_processinggemによって提供されるメソッドです。画像を300x300に縮小するだけです 大きい場合は何もせず、小さい場合は何もしません。さらに、元のアスペクト比を維持します。

これで、画像を表示するときに、:originalのいずれかを指定する必要があります。 または:thumb image_urlへの引数 方法:

views / photos / _photo.html.erb

<div>
  <% if photo.image_data? %>
    <%= image_tag photo.image_url(:thumb) %>
  <% end %>
  <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p>
</div>

フォーム内でも同じことができます:

views / photos / _form.html.erb

<% if @photo.image_data? %>
    <%= image_tag @photo.image_url(:thumb) %>
    Remove attachment: <%= f.check_box :remove_image %>
<% end %>

アップロードの完了後に処理済みファイルを自動的に削除するには、delete_raw:

というプラグインを追加します。

models / image_uploader.rb

plugin :delete_raw

画像のメタデータ

実際に画像をレンダリングする以外に、そのメタデータを取得することもできます。たとえば、元の写真のサイズとMIMEタイプを表示してみましょう。

views / photos / _photo.html.erb

<div>
  <% if photo.image_data? %>
    <%= image_tag photo.image_url(:thumb) %>
    <p>
      Size <%= photo.image[:original].size %> bytes<br>
      MIME type <%= photo.image[:original].mime_type %><br>
    </p>
  <% end %>
  <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p>
</div>

その寸法はどうですか?残念ながら、デフォルトでは保存されませんが、これはstore_dimensionsというプラグインで可能です。

画像のサイズ

store_dimensionsプラグインはfastimagegemに依存しているため、今すぐ接続してください:

gem 'fastimage'

実行することを忘れないでください:

bundle install

プラグインを含めるだけです:

models / image_uploader.rb

plugin :store_dimensions

そして、 widthを使用して寸法を表示します およびheight メソッド:

views / photos / _photo.html.erb

<div>
  <% if photo.image_data? %>
    <%= image_tag photo.image_url(:thumb) %>
    <p>
      Size <%= photo.image[:original].size %> bytes<br>
      MIME type <%= photo.image[:original].mime_type %><br>
      Dimensions <%= "#{photo.image[:original].width}x#{photo.image[:original].height}" %>
    </p>
  <% end %>
  <p><%= photo.title %> | <%= link_to 'Edit', edit_photo_path(photo) %></p>
</div>

また、寸法があります 幅と高さを含む配列を返す使用可能なメソッド(たとえば、 [500、750]

クラウドへの移行

開発者はアップロードされたファイルをホストするためにクラウドサービスを選択することが多く、Shrineはそのような可能性を示しています。このセクションでは、AmazonS3にファイルをアップロードする方法を紹介します。

最初のステップとして、さらに2つのgemを Gemfileに含めます。 :

gem "aws-sdk", "~> 2.1"
group :development do
    gem 'dotenv-rails'
end

S3のSDKを使用するにはaws-sdkが必要ですが、dotenv-railsは開発中の環境変数を管理するために使用されます。

bundle install

先に進む前に、APIを介してS3にアクセスするためのキーペアを取得する必要があります。取得するには、Amazon Web Services Consoleにサインイン(またはサインアップ)して、セキュリティクレデンシャル>ユーザーに移動します。 。 S3でファイルを操作する権限を持つユーザーを作成します。 S3へのフルアクセスを提示する簡単なポリシーは次のとおりです:

{
  "Version": "2016-11-14",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}

作成したユーザーのキーペアをダウンロードします。または、ルートアクセスキーを使用することもできますが、強くお勧めしません 非常に安全ではないので、そうすることはできません。

次に、ファイルをホストするS3バケットを作成し、プロジェクトのルートにファイルを追加して構成をホストします。

.env

S3_KEY=YOUR_KEY
S3_SECRET=YOUR_SECRET
S3_BUCKET=YOUR_BUCKET
S3_REGION=YOUR_REGION

絶対に公開しないでください このファイルを公開し、Gitから除外するようにしてください:

.gitignore

.env

次に、Shrineのグローバル構成を変更し、新しいストレージを導入します。

config / initializers / shrine.rb

require "shrine"
require "shrine/storage/s3"

s3_options = {
    access_key_id:      ENV['S3_KEY'],
    secret_access_key:  ENV['S3_SECRET'],
    region:             ENV['S3_REGION'],
    bucket:             ENV['S3_BUCKET'],
}

Shrine.storages = {
    cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
    store: Shrine::Storage::S3.new(prefix: "store", **s3_options),
}

それでおしまい!アプリの他の部分に変更を加える必要はなく、この新しいストレージをすぐにテストできます。間違ったキーに関連するS3からエラーを受け取った場合は、末尾のスペースや非表示の特殊記号を使用せずに、キーとシークレットを正確にコピーしたことを確認してください。

結論

この記事は終わりです。うまくいけば、今ではあなたは神社を使用することに非常に自信を持っており、あなたのプロジェクトの1つでそれを使用することを熱望しています。このgemの機能の多くについて説明しましたが、ファイルとともに追加のコンテキストを保存する機能や直接アップロードメカニズムなど、さらに多くの機能があります。

したがって、Shrineのドキュメントと、利用可能なすべてのプラグインについて詳しく説明している公式Webサイトを参照してください。この宝石について他に質問がある場合は、遠慮なく投稿してください。一緒にいてくれてありがとう、また会いましょう!


  1. RailsでのTailwindCSSの使用

    CSSは魔法のようですが、時間がかかります。美しく、機能的で、アクセスしやすいサイトを使用するのは楽しいことですが、独自のCSSを作成するのは大変です。 Bootstrapなどの多くのCSSライブラリは近年爆発的に増加しており、Tailwindは2021年にパックをリードしています。 RailsにはTailwindが付属していませんが、この記事では、TailwindCSSを新しいRubyon Railsプロジェクトに追加する方法を説明します。これにより、設計の実装にかかる時間を節約できます。また、Tailwindのユーティリティクラスを使用した設計のウォークスルーも行います。このチュートリア

  2. Rails5でのAngularの使用

    あなたは前にその話を聞いたことがあります。分散型で完全に機能するバックエンドAPIと、通常のツールセットで作成されたフロントエンドで実行されているアプリケーションがすでにあります。 次に、Angularに移動します。または、AngularをRailsプロジェクトと統合する方法を探しているだけかもしれません。これは、この方法を好むためです。私たちはあなたを責めません。 このようなアプローチを使用すると、両方の世界を活用して、たとえばRailsとAngularのどちらの機能を使用してフォーマットするかを決定できます。 構築するもの 心配する必要はありません。このチュートリアルは、この目的のた