タケユー・ウェブ日報

Ruby on Rails や Flutter といったWeb・モバイルアプリ技術を武器にお客様のビジネス立ち上げを支援する、タケユー・ウェブ株式会社の技術ブログです。

Refileを使ってハイテクなアップローダ(機能を利用したサンプル)を作ってみたのでメモ

Refile

昨日知ったのですが、Refileというファイルアップロード用のgemがとてもナウい感じです。

github.com

id:willnetさんとかが記事にしてました。

blog.willnet.in

diary.mansion-market.com

ざっと触ってみて、そのうち使えそうだったのでメモ。

作ったサンプル

Readmeほぼそのまま

takeyuweb/refile-sample · GitHub

基本

f:id:uzuki05:20150706223423p:plain

f:id:uzuki05:20150706223426p:plain

普通のアップロード機能を実装しました。

takeyuweb/refile-sample at 6a7dcbf5fc867e7825c99769748fca27649a9a8d · GitHub

サムネイル表示忘れましたが、ヘルパが用意されています。

Sinatra製の画像サーバが内蔵されていて、そちらで"表示時の"リサイズ、クリッピング等に対応します。Rails利用時には自動的にマウントされるようです。

利用する場合はフロントキャッシュ等と組み合わせることが必須になりますが、とても便利ですね。見たところキャッシュ用のHTTPヘッダもきちんと使用されているようですので、リバースプロキシとしてnginxを使っているならとても簡単に設定できることでしょう。

あとはCloudFrontのカスタムオリジンあたりが簡単でしょう。

S3への保存

f:id:uzuki05:20150706223430p:plain

今回保存先をS3にしたのでそちらにアップロードされました。

Refile.store = Refile::S3.new(prefix: "store", **aws)

確認画面を実装してみる

公式の方法はわからなかったので、コードを読んで適当に実装しました。適当なのでこれではうまくいかないケースもあるかもです。

確認画面 · takeyuweb/refile-sample@cb248dd · GitHub

    <% if f.object.confirming? %>
        <div class="field">
            <%= f.label :name %><br>
            <%= f.object.name %>
            <%= f.hidden_field :name %>
        </div>

        <% if f.object.file.present? %>
            <div class="field">
                <%= f.hidden_field :file, value: f.object.file_attacher.data.to_json %>
                <%= image_tag attachment_url(f.object, :file, :fill, 200, 200) %>
            </div>
        <% end %>

file_attacherの所は現在のmasterではfile.attacherになるみたいです。

これで無事確認画面で画像のプレビューができました。

なお、レコードを保存する前、アップロードしただけの状態のファイルはcacheと呼ばれます。

Refile.cache = Refile::S3.new(prefix: "cache", **aws)
Refile.store = Refile::S3.new(prefix: "store", **aws)

今回、上記のようにして、実際の保存先及びcacheのどちらもS3に設定したので、cacheもS3上にあります。

f:id:uzuki05:20150706223433p:plain

これの何が嬉しいかというと、確認画面を実装した場合に、保存先がローカルストレージだと複数台のアプリサーバで構築しているシステムの場合に、確認画面と保存で別々のサーバに割り振られ、うまくいかなくなる可能性があります。

carrierwaveの場合、今日現在普通にインストールできるバージョン(0.10.0)ではこのようなケースでうまく行かないのですが、Refileの場合は現在バージョン(0.5.5)でも問題なくS3を利用した場合でも確認画面を実装できました。

ファイル削除に対応

f:id:uzuki05:20150706233921p:plain

ファイル削除に対応 · takeyuweb/refile-sample@d9ff8be · GitHub

Readmeの通りなのですが、先に実装した確認画面で、削除にチェックを入れた場合に表示したくなかったので、次のようにRefile::Attacher#remove?を使ってみました。例によってドキュメントにないので正しい方法かはわかりません。

    <% if f.object.confirming? %>
        <div class="field">
            <%= f.label :name %><br>
            <%= f.object.name %>
            <%= f.hidden_field :name %>
        </div>

        <% if f.object.file.present? %>
            <% unless f.object.file_attacher.remove? %>
                <div class="field">
                    <%= f.hidden_field :file, value: f.object.file_attacher.data.to_json %>
                    <%= image_tag attachment_url(f.object, :file, :fill, 200, 200) %>
                </div>
            <% end %>
        <% end  %>
        <%= f.hidden_field :remove_file %>

ダイレクトアップロード

ドキュメントまま。

RefileのJavaScriptライブラリを使うことで、ヘルパにdirect: trueオプションを追加するだけで、ファイルを選択されたら直接アップロードを開始できるようになります。ユーザーの待ち時間を減らすことができますね。

コントローラやモデルへの変更は一切不要です。

バックグラウンドアップロード · takeyuweb/refile-sample@95fa658 · GitHub

<%= f.attachment_field :file, direct: true %>

Presigned uploads(S3への署名付き URL を使用したオブジェクトのアップロード)

ヘルパにpresigned: trueオプションを追加するだけで、サーバを介さずに、ブラウザから直接S3へアップロードできるようになります。

それも、コントローラやモデルへの変更は一切不要、通常のアップロードと同じように扱えるのが素晴らしいです。

Presigned uploads · takeyuweb/refile-sample@562922d · GitHub

署名付き URL を使用したオブジェクトのアップロードについてはこちらを。

署名付き URL を使用したオブジェクトのアップロード(AWS SDK for Ruby) - Amazon Simple Storage Service

また、この機能は先のダイレクトアップロード機能と併用することもでき、

<%= f.attachment_field :file, direct: true, presigned: true %>

このようにすると、ファイルを選択した時点ですぐにS3に直接アップロードされます。

仕組みとしては、ビューのレンダリングの際に、S3の署名付きURLを発行してdata属性にセットしておき、onchangeハンドラで使用する、というもののようです。なるほど、勉強になります。

ファイル種類の検証

拡張子MIMEタイプ、MIMEタイプのグループ(デフォルトで提供されるものほか、独自に定義可能)の制限が簡単にできます。

File type validations · takeyuweb/refile-sample@4342f60 · GitHub

複数ファイルのアップロード

f:id:uzuki05:20150707000051p:plain

f:id:uzuki05:20150707000058p:plain

f:id:uzuki05:20150707000105p:plain

input type="file" multipleを使って複数選択したファイルを一括アップロードでき、それを一対多で保存できます。

Multiple file uploads · takeyuweb/refile-sample@ef11448 · GitHub

この機能も、ダイレクトアップロード、署名付きURLでのアップロードにも対応しており、以下のようにして利用できます。

<%= f.attachment_field :images_files, multiple: true, direct: true, presigned: true %>

複数アップロードと、ダイレクトアップロード、署名付きURLでのアップロードの併用 · takeyuweb/refile-sample@014cc41 · GitHub

※2015.7.6現在の最新リリース(0.5.5)には含まれていないので、使いたいならgithubからインストールする。

gem 'refile', require: 'refile/rails', github: 'refile/refile'