アナログCPU:5108843109

ゲームと音楽とプログラミング(酒と女とロックンロールのノリで)

('ω') < イザユケエンジニャー

ドラッグ&ドロップでファイルをアップロードする(最低限版)

タイトル通りのやつにちょっと苦戦したのでメモ。
つーか業務アプリなんだからドラッグ&ドロップじゃなくてよくね??

今回は必要最低限の処理だけ書いた版です。ちょっとリッチ版はこちら。
ドラッグ&ドロップでファイルをアップロードする(ちょっとリッチ版) - アナログCPU:5108843109

ほぼ完成サンプル

<HTML>

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD;">うp</div>

<form enctype="multipart/form-data" action="【※】" method="post">
  <input id="upload-form-fileselect" type="file" name="files[]" multiple="multiple">
  <input type="submit" value="送信">
</form>

【※】の部分はファイルの送信先
例えば「/upload.php」に受け取る処理を作るなら「/upload.php」と指定すればOK。
今回はアップロード後の処理まで書きませんが、
とりあえず確認する分には

<?php 
var_dump($_FILES);

とだけ書いたupload.phpでも用意しときゃいいんじゃないかな。

<JS>

$(document).ready(function(){
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
    });
});

わーい意外とシンプル。


以下メイキングです。

<HTML>まずは普通にファイル送信フォームを用意しておく

以前自分で作ったものを参考に。
http://honey8823.hateblo.jp/entry/2018/07/04/145326

今回はせっかくドラッグ&ドロップでファイルをアップロードするので、
複数ファイルを受け取れるようにします。

<form enctype="multipart/form-data" action="【※】" method="post">
  <input type="file" name="files[]" multiple="multiple">
  <input type="submit" value="送信">
</form>

【※】の部分はファイルの送信先
例えば「/upload.php」に受け取る処理を作るなら「/upload.php」と指定すればOK。

<HTML>ドラッグ&ドロップするエリアを作る

フォームが動くのを確認したら、
ファイルをドラッグ&ドロップするためのエリアを追加します。
styleはまあとりあえず見てわかるくらいのレベルにしておいて、
明らかに使うであろうidを付与しておきます。

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD;">うp</div>

<form enctype="multipart/form-data" action="【※】" method="post">
  <input type="file" name="files[]" multiple="multiple">
  <input type="submit" value="送信">
</form>

<JS>ドラッグ&ドロップしたときのイベント設定

エリアはできたので、次はそこでファイルを受け取ることを目指します。

clickイベントみたいな感じで、
「dragenter」で指定部分にドラッグしたときの処理、
「dragover」で指定部分の上に乗っかっているときの処理、
「dragleave」で指定部分から離れたときの処理、
drop」でドロップ時の処理になるようなので
ログだけ表示するようにして設置し、動作を確認してみます。

$(document).ready(function(){
    // 指定部分にドラッグしたときの処理
    $("#upload-area").on("dragenter", function(e){
        console.log("DragEnter");
    });

    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        console.log("DragOver");
    });

    // 指定部分から離れたときの処理
    $("#upload-area").on("dragleave", function(e){
        console.log("DragLeave");
    });

    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        console.log("Drop");
    });
});

適当なファイルをdivのエリアまでドラッグしてみると、
指定部分に入った瞬間に「dragenter」が、
乗っかっている間ずっと「dragover」が動いていました。
そこから離れると「dragleave」が動きますし、

離れずにドロップすると、掴んでいたファイルがブラウザで開きました。

…だよね!!わかる!!!

<JS>ドロップ時にファイルが開かないようにする

「dragover」と「drop」のイベントに対して、preventDefault()します。

あと、今回「dragenter」「dragleave」は使わさそうなので一旦削除します。
(ちょっとリッチ版で再登場します)

$(document).ready(function(){
    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });
    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
    });
});

これでもう一度試してみると、ドロップ時にファイルを開くこともなく、
きちんと「Drop」のログが出て、動いていることが確認できました。

しかしそもそもpreventDefaultって何、と思って調べてみましたが、いまいちピンと来ず。
「その要素のイベントをキャンセルするもの」らしいですが、何をどこまでキャンセルするんだろう…。

<JS>ドロップ時にファイルを受け取る

さて、もちろん今のままだと「ファイルをドラッグ&ドロップしても開かないだけ」のものなので、
ドロップ時に掴んでいたファイルを受け取れるようにします。

$(document).ready(function(){
    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });
    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        var files = e.originalEvent.dataTransfer.files;
    });
});

drop」時の処理に、files変数にドロップしたファイルを入れる処理を加えました。
これだけで、複数投げ込むこともできます。
files変数には「ファイル名」「最終更新日」「サイズ」「ファイルタイプ」などが入ります。
例えばこんな感じ。

files = [
    {
        lastModified: 1535505322039,
        name: "hoge.txt",
        size: 4,
        type: "text/plain",
    },
    {
        lastModified: 1530675419721,
        name: "fuga.csv",
        size: 18,
        type: "application/vnd.ms-excel",
    },
]

<HTML+JS>受け取ったファイルをformに渡す

ここで受け取ったファイル情報を最初に作ったformに渡せば、あとの処理は一緒のはず。
ということで、formタグ内のfile用inputタグにidを付けてやります。

<HTML>

<div id="upload-area" style="height: 100px; width: 100px; background: #DDD;">うp</div>
<form enctype="multipart/form-data" action="【※】" method="post">
  <input type="file" id="upload-form-fileselect" name="files[]" multiple="multiple">
  <input type="submit" value="送信">
</form>

<JS>

$(document).ready(function(){
    // 指定部分の上に乗っかっているときの処理
    $("#upload-area").on("dragover", function(e){
        e.preventDefault();
    });
    // 指定部分でドロップしたときの処理
    $("#upload-area").on("drop", function(e){
        e.preventDefault();
        document.getElementById("upload-form-fileselect").files = e.originalEvent.dataTransfer.files;
    });
});

files変数に入れるのをやめて、inputタグの「.files」に渡すようにしました。
何故か
「$("#upload-form-fileselect").files = e.originalEvent.dataTransfer.files;」
だと動かなかった。
jQueryセレクタだと「.files」に対応してないのかな??
(詳しい人教えてください)

何はともあれ、これでドラッグ&ドロップしてみると、
通常のファイル選択後と同じような表示になるのを無事確認できました。

「送信」ボタンクリックでアップロードできたことも確認。めでたしめでたし。