[PHP]ドラッグ&ドロップでマルチファイルアップロードまとめ(js:FileList,DataTransferなど)

php

最近アップロード関連の機能を久しぶりに作ることがありました。

ドラック&ドロップでのファイルのマルチアップロード機能について、
サンプルソースや懸念事項など、まとめてみたいと思います。
サーバーサイドはPHPで書いてます。

フォームを書く

フロントのHTML&Javascriptは、こんな感じになるかと思います。
javascriptにはdrag&dropした時の挙動を書いてます👌

<html lang="ja">
  <head>
    <title>multi upload test</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" c
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" cros
    <script>
    $(function(){
      // ドラッグしたままエリアに乗った&外れたとき
      $(document).on('dragover', '#file_drag_drop_area, #file_drag_drop_area_stl', function (event) {
          event.preventDefault();
          $(this).css("background-color", "#999999");
      });
      $(document).on('dragleave', '#file_drag_drop_area, #file_drag_drop_area_stl', function (event) {
          event.preventDefault();
          $(this).css("background-color", "transparent");
      });

      // ドラッグした時
      $(document).on('drop', '#file_drag_drop_area', function (event) {
          let org_e = event;
          if (event.originalEvent) {
              org_e = event.originalEvent;
          }

          org_e.preventDefault();
          file_input.files = org_e.dataTransfer.files;
          $(this).css("background-color", "transparent");
      });                                                                                                                                                                        
    });
    </script>
  </head>
  <body>
    <div class="mt-3">
      <form id="file_upload_form" method="post" enctype="multipart/form-data" action="upload.php">
        <div id="file_drag_drop_area" class="text-center p-3 rounded col-md-10 mx-auto" style="border:3px #000000 dashed;">
          ここにファイルをドラッグ&ドロップ<br/>
          <span>または</span><br/>
          <input id="file_input" type="file" name="file[]" multiple />
        </div>
        <div class="d-flex justify-content-center mt-2">
          <input type="submit" value="送信" class="btn btn-primary pl-3 pr-3" />
        </div>
      </form>
    </div>
  </body>
</html>

危険なので、デモではアップロードできないようにしています😅
ここにDEMOフォームを置いてます

HTMLフォームでの注意点

HTMLのフォームに関しては、 enctype="multipart/form-data" をつけ忘れると、アップロードできないので注意が必要です。
input type="file" でファイルの入力フォームを作れますが、複数の場合はname属性の箇所を[] で配列にして、multiple 属性を付けておきます。

javascriptでの注意点

event.preventDefaultについて

ドラッグドロップのエリアを目立たせるため、jQueryでドラッグして上に乗った時の処理(dragover)とドラッグエリアから外れた時の処理(dragleave)を書いていますが
event.preventDefault();ブラウザデフォルトの動きをキャンセルしています。これを書いてないと、上手く動きません。
例えばこの処理を書かずに、画像をクロームに乗せたときは、その画像がそのまま表示されます。。🤦‍♂️

event.originalEventについて

jqueryのイベント(event)からはdataTransferを取得することが出来ないので、DOMイベントを取得するためにoriginalEventから取ってきています。
dataTransfer.files にドロップしたファイルの情報が入っていて、それをDOMのid=”file_input”にセットしています。

javascriptでこれがやりたい

フォームにセットしたファイル名が見たい

filesにファイルのデータが入ってます(FileListオブジェクトになってます)ので、そのnameプロパティを参照します。
これを使うと、拡張子のチェックが出来ます。

for(let i = 0; i < file_input.files.length; i++) { 
  console.log(file_input.files[i].name); 
}

一部ファイルを削除したい

delete file_input.files[0]; とかで消せそうな気がしますが、消えません・・・
DataTransferを通さないと、内容を変更できないようです。

DataTransferを新しく作って、必要なファイルだけaddし、その後元のfilesを上がきすればOKです。

let data_transfer = new DataTransfer();
data_transfer.items.add(file_input.files[1]);
data_transfer.items.add(file_input.files[2]);

file_input.files = data_transfer.files;

PHP(フレームワークなしで書く)

formのactionで指定している箇所で処理をするので、upload.phpを用意して下記のような感じにします。
アップロードしたファイルのバリデーションなども必要かと思いますが、とりあえずアップロードのみです。

<?php
// uploadするディレクトリ
$upload_dir = './';

for ($i=0; $i < count($_FILES['file']['name']); $i++) {
  $uploadfile = $upload_dir . basename($_FILES['file']['name'][$i]);

  if (move_uploaded_file($_FILES['file']['tmp_name'][$i], $uploadfile)) {
      echo "successfully uploaded.<br>";
  } else {
      echo "upload faild<br>";
  }
}

アップロードされると、$_POSTではなく$_FILESに情報が入って、バックエンドに渡ってきます。
気をつけるのは$_FILES[‘file’]が配列になるのかと思いきや、$_FILES[‘file’][‘name’]とか$_FILES[‘file’][‘tmp_name’]が配列になる所ですね。

move_uploaded_fileでphpのtemp位置から(第一引数)、指定した場所(第二引数)に移動してくれます。

まとめ

気をつけるところをまとめると、
・フォームの書き方に気を付ける。
・ドラッグ&ドロップさせるときは、event.preventDefault(); を付けてブラウザデフォルトの挙動をキャンセルする。
・jqueryのevent引数からfilesは取れないので、originalEventを使う。
・FileListオブジェクトの扱いに気を付ける、

みたいな感じですかね。
それでは👋

コメント

タイトルとURLをコピーしました