webページにローカルファイルをドロップ可能にするjQueryプラグインを作ってみた

こんにちは。
セミが鳴き出しましたね。夏、嫌な季節です。

さて、Macで良いリネームアプリを探しているのですが、
なかなか決定版が見つからずに嘆いています。
そんな時ふと思いました。

(使ったことないけど)HTML5のFileAPI使えばリネームって出来るんじゃないか?

あと、いちいちファイルをチューザーからファイルを選択するのも面倒だし、
HTML5のDrag&DropAPI使ってブラウザにファイルをドロップできるようにしよう!

と思いFileAPIとDrag&DropAPIを調べてみたら、
ちょっとした小物を作るのにも地味に面倒だったのでメモを残します。

File APIとは?

File APIとは、HTML5で提供されているAPIです。
ローカルファイルやディレクトリの読み書きが出来るオブジェクトです。

例えば、バイナリデータを扱うBlobや、
ファイルの読み取りを行うFileReaderなどがあります。

ファイルの書き込みが出来るインタフェースもあるのですが、
まだChromeのみしか実装を終えておらず、限定的な環境だったので、
リネームツールは諦めて、単にファイルの情報を読取ることを目的としました。

File API (日本語訳)
File API (Windows)

Drag&Drop APIとは?

Drag&Drop APIとは、ページ内の要素をドラッグ可能にしたり、
そのまま要素へのドロップを可能にするAPIです。

ウェブページ内の要素やローカル環境に保存されたファイルなどのデータを、
マウスで引きずるように移動させて他の場所に置く操作のことです。
HTML5以前にも、mousedownやmouseupなどのイベントで実現することはできましたが、
HTML5ではドラッグ&ドロップ専用の新しいイベントや新しいメソッド・属性が追加されています。
ドラッグ&ドロップ-HTML5のAPI、および、関連仕様

ブラウザにファイルをドロップできるようにする

簡単に各APIの概要を追ったところで、本題です。
通常、ブラウザへファイルをドロップすると、そのファイルが開かれると思います。

画像をドロップすればその画像が表示されます。
プログラムやCSSのファイルならその内容が表示されます。
HTMLファイルをドロップすれば、そのHTMLを解釈した結果が見れると思います。

このようにファイルをブラウザにドロップする操作は、
イベントなのでjsでキャンセルができます

つまりどういうことかと言うと、
サンプルページを用意したので、適当なファイルをドロップしてみて下さい。

ファイルのドロップをキャンセルするサンプル

如何でしょう。 枠の中にファイルをドロップしても何も起こらないと思います。

従来ブラウザにファイルをドロップした時に起きるイベントを、
すべてキャンセルしています。

まとめると、
ブラウザのデフォルトの挙動をキャンセルして、
独自のイベントハンドラを書くことで、
ファイルがドロップされたことを検知し、扱うことが可能
になる
と言うことです。

ドロップされたファイルを読み取る

先ほどのサンプルで、
ファイルのドロップイベントを受け取ることが出来るようになったので、
次はドロップされたファイルを読み取ってみようと思います。

ただ単にファイル名、ファイルサイズ、ファイルの種類を取得するだけなら、
FileReaderを使わずに取得できます。

ただし、ファイルの内容を読み取るには、
FileAPIのFileReaderインタフェースを使います。

ざっとサンプルコードを。
動作確認はこちらからできます。

ブラウザにファイルをドロップしてその内容を読み取るサンプル

ドロップされたファイルの取得

ドロップされたファイルは、dropイベントに渡されるeventオブジェクトから取得出来ます。
e.originalEvent.dataTransfer.files
でドロップされたファイルにアクセスできます。

filesと複製形になっていることから分かるように、
複数のファイルをまとめてドロップしても、まとめて取得できます

ファイル取得時の注意点

取得したファイルは、FileListオブジェクトというものになっており、
配列のように各要素にブラケット([])でアクセスできますが、
配列ではありません

2013070

こんな感じ。

関数に渡されるargumentsのようなオブジェクトです。
なので、配列のメソッドをそのまま使うことができません。

2013070

エラー。

ただ、argumentsと同じように、callを使うことで配列に変換出来ます。
用途によりますが、配列にしておいたほうが使える範囲が広がると思います。

var tmp = e.originalEvent.dataTransfer.files, files = Array.prototype.slice.call(tmp); console.log(Array.isArray(tmp)); // false console.log(Array.isArray(files)); // true

各イベントの説明

おそらくイベント名を見ればお察しかと思いますが、
このサンプルで使っているイベントの簡単な説明を。

  • dragenter
    • ドラッグした状態でマウスカーソルが要素に入ったとき
  • dragover
    • ドラッグした状態でマウスカーソルが要素の上を移動させたとき
  • dragleave
    • ドラッグした状態でマウスカーソルが要素から外れたとき
  • drop
    • ドラッグしたものが要素の中でドロップされたとき
  • FileReader.onload
    • FileReader.readAsTextなどでファイルの読み取りが完了したとき
    • 読み込んだ内容はe.target.resultで取れます

jQuery.file-drop.js

これらのサンプルを手軽に利用できるように、jQueryプラグイン化してみました。
サンプルはこちらから見れます。

先ほどのサンプルのまんまでは面白く無いので、
画像がドロップされたら、その画像を表示するという処理を入れてみました。

文字列から画像を復元する方法については、また別途記事を書こうと思います。

使い方はこんな感じです。

jQuery.filedrop.jsは4つのオプションを提供します。

  • dragEnter
    • ブラウザのデフォルトの挙動をキャンセルしている以外、dropenterイベントと同じです
  • dragLeave
    • ブラウザのデフォルトの挙動をキャンセルしている以外、dropleaveイベントと同じです
  • drop
    • ファイルがドロップされた際に実行される
    • 引数はドロップされたファイルの配列
  • dropEach
    • ファイルが1つドロップされた際に実行される
    • 複数のファイルがドロップされた場合は、ファイルの個数分呼び出される
    • 第一引数にドロップされたFileオブジェクト
    • 第二匹数にファイルの内容の文字列が渡される

まとめ

ファイルの読み取りは簡単なのに、書き込みがファイルの面倒でした。。。

ドラッグ&ドロップについては、
APIがなくても、mouseoverやmousedownを使って書けますが、
APIを用いたほうが可読性が高く柔軟なコードになると思います。

環境に制限が無いなら、こちらを使ってみるといいと思います。

最後に、jQuery.filedrop.jsのソースはGistに上げてあります。
最期まで読んで頂き、ありがとうございました!

jquery.filedrop.js

参考リンク

JavaScript でのローカル ファイルの読み込み – HTML5 Rocks