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

## Refile

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

https://github.com/refile/refile

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

http://blog.willnet.in/entry/2015/01/05/021926

http://diary.mansion-market.com/727/

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


### 作ったサンプル

[Readme](https://github.com/refile/refile/blob/master/README.md)ほぼそのまま

https://github.com/takeyuweb/refile-sample


#### 基本

![01.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/7fc7d882-ac80-ef80-7aba-21b2674a77be.png)

![02.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/bb931d05-b589-f67f-df9f-c90aef85e96d.png)

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

https://github.com/takeyuweb/refile-sample/tree/6a7dcbf5fc867e7825c99769748fca27649a9a8d

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

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

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

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

#### S3への保存

![03.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/10a020a6-281a-f53d-e85c-1d05fa95d9df.png)

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

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

### 確認画面を実装してみる

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

https://github.com/takeyuweb/refile-sample/commit/cb248dddefae9cf47c5674c217b6950893f74b43

```erb
    <% 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と呼ばれます。

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

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

![04.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/72f63717-4643-f2a9-408f-f81b61d0d6ab.png)

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

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


### ファイル削除に対応 

![05.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/aaf6aa11-2206-aeae-a9a4-c36a6340c0ba.png)


https://github.com/takeyuweb/refile-sample/commit/d9ff8be3ca39565e43418e0c6615ca006fab6f77

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

```erb
    <% 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`オプションを追加するだけで、<b>ファイルを選択されたら直接アップロードを開始できる</b>ようになります。ユーザーの待ち時間を減らすことができますね。

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

https://github.com/takeyuweb/refile-sample/commit/95fa65876354e5b614427e1767f6e45e092f7839

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

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

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

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

https://github.com/takeyuweb/refile-sample/commit/562922dd516b34483689ff54c8cfb8a7948efad3

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

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UploadObjectPreSignedURLRubySDK.html

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

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

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

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

### ファイル種類の検証

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

https://github.com/takeyuweb/refile-sample/commit/4342f608c9005870199fd9309ed50b0a2697bb2e


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

![06.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/90c7f40d-771b-7427-a5e5-905e8442b10b.png)

![07.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/3e2e8146-20d0-0d21-61fb-82c42b193140.png)

![08.PNG](https://qiita-image-store.s3.amazonaws.com/0/7000/1a76fe4f-4d7f-a844-9dac-eb080c047f6f.png)


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

https://github.com/takeyuweb/refile-sample/commit/ef1144820a38e0659ee135f299fd4f139c94607a

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

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

https://github.com/takeyuweb/refile-sample/commit/014cc410e68412d8b3f522169428d9e9ddebcbb1


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

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