Edited at

Rails アプリでファイルアップロードに Dropzone.js を使う

More than 3 years have passed since last update.


概要

Dropzone.js という JavaScript ライブラリがクールだったので、これを Rails アプリケーションで利用する方法をご紹介します。


Dropzone.js について

ドラッグ & ドロップによるファイルアップロード機能を提供する JavaScript ライブラリです。通信は Ajax で行われます。

この形式は PC 向けの Web アプリケーションでは結構見かけると思います。この Qiita でも使われていますね。

Dropzone.js 見た目は以下のとおりです。なお、デフォルトでは外枠は実線ですが、公式サイトのデモに合わせて CSS で破線にしています。

スクリーンショット 2016-03-09 10.46.23.png

ここにファイルをドラッグ & ドロップするとこのようになります。プレビューとして、画像ファイルはサムネイルが表示され、ドキュメントファイルはファイルサイズやファイル名などの情報が表示されます。

スクリーンショット 2016-03-09 09.09.55.png

また、画像ファイルにマウスカーソルをホバーすると、ドキュメントファイルと同様にファイル情報が表示されます。

スクリーンショット 2016-03-09 09.10.13.png

おしゃれですね :heartpulse:

ちなみに、ファイルは複数同時にドラッグ & ドロップすることが可能です。


導入

では実際に Rails アプリケーションに導入したいと思います。

dropzonejs-rails という Gem が公開されているので、まずはそれをインストールします。


Gemfile

# 2016/03/09 時点での最新バージョンは 0.7.3 です。

gem 'dropzonejs-rails', '~> 0.7.3'

$ bundle install

そして application.jsapplication.css に以下を追記します。


app/assets/javascripts/application.js

//= require dropzone



app/assets/stylesheets/application.css

*= require dropzone/basic

*= require dropzone/dropzone

次に View を実装します。まず例として、僕の Rails アプリケーションでは、ファイルアップロードのために以下のような Model と Controller を実装済みと仮定します。特定の書類 (Document) に対して添付ファイル (Attachment) を作成するイメージです。


app/models/document.rb

class Document < ActiveRecord::Base

has_many :attachments, dependent: :destroy
end


app/models/attachment.rb

class Attachment < ActiveRecord::Base

belongs_to :document

# この例ではサーバサイドでのファイルアップロードに Paperclip という Gem を利用している想定です。
# ただこの記事はサーバサイドでのファイルアップロード方法には関係しないので、
# お好きな Gem をご利用ください。
has_attached_file :file
end



app/controllers/attachments_controller.rb

class AttachmentsController < ApplicationController

def create
@attachment = Attachment.create!(create_params)
end

private

def create_params
params.require(:attachment).permit(:document_id, :file)
end
end


Dropzone.js 用の View は form_tag を使って以下のように実装します。


app/views/documents/edit.html.erb

<%= form_tag(attachments_path, class: 'dropzone', id: 'upload-dropzone') do %>

<div class="fallback">
<%= file_field_tag('attachment[file]') %>
</div>
<% end %>

なお、file_field_tag の第 1 引数、つまり input 要素の name 属性は JavaScript 側で明示する必要があります。さもないと attachment[file]file になってしまいます。そのため、実際は file_field_tag の引数は任意の値で構いません。

次にクライアントサイドを実装します。

Dropzone.autoDiscover = false

new Dropzone '#upload-dropzone',
uploadMultiple: false
paramName: 'attachment[file]'
params:
'attachment[document_id]': 123
init: ->
@on 'success', (file, json) ->
# アップロード成功時の処理をここに実装します。
dictDefaultMessage: '''
<i class="fa fa-file-o fa-2x"></i><br>
<br>
ファイルをここにドロップするか<br>
ここをクリックして下さい
'''

パラメータを { attachment: { file: ... } という具合にネストさせたいので paramName を指定します。また、他にもパラメータを渡す必要がある場合は params オプションで指定します。ちなみに、この params オプションは 公式サイトの説明 に載っておらず、GitHub の Issue で知りました。

最後に CSS で見た目を調整します。最初に述べたとおり、僕はボーダーを破線にしたり角を丸くしたりしています。また、Dropzone の中に表示される文言の表示も調整しています。

@import 'compass/css3/border-radius';

.dropzone {
border: 2px dashed #b4bcc2;
@include border-radius(4px);

.dz-message {
font-size: 16px;
text-align: center;
margin: 0
}
}

以上で実装は完了です。


おまけ


プレビューを非表示にする

プレビューを表示したくない場合はこのようにします。

Dropzone.autoDiscover = false

new Dropzone '#upload-dropzone',
uploadMultiple: false
paramName: 'attachment[file]'
params:
'attachment[document_id]': 123
init: ->
@on 'success', (file, json) ->
# アップロード成功時の処理をここに実装します。
dictDefaultMessage: '''
<i class="fa fa-file-o fa-2x"></i><br>
<br>
ファイルをここにドロップするか<br>
ここをクリックして下さい
'''

previewTemplate: '<div style="display:none"></div>' # このオプションを追加します。

// アップロード後も dictDefaultMessage の文言を表示したままにします。

.dropzone.dz-started .dz-message {
display: block;
}


参考