ドラッグ&ドロップでマルチファイルアップロードのサンプルと懸念事項

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

ドラック&ドロップでのファイルのマルチアップロード機能について、 サンプルソースや懸念事項など、まとめてみたいと思います。 サーバーサイドは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
    https://code.jquery.com/jquery-3.3.1.slim.min.js
  </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オブジェクトの扱いに気を付ける、

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

コメント