d'n'd 如何通过 Ajax 传输文件(或文件夹)?

How transfer files (or folders) via Ajax by d'n'd?

我想通过 AJAX 将文件夹上传到服务器。但是,我在上传文件时遇到了麻烦。

我使用 e.dataTransfer.itemswebkitGetAsEntry() 检查 - 是文件还是文件夹?

如果是文件,在函数 traverseFileTree 中我得到了文件,但是 我不能将它附加到 formData

如果我使用e.dataTransfer.files,我不知道它是什么。文件或文件夹,因为 webkitGetAsEntry() 出错。

我做错了什么? 如何将文件传输到全局数组 $_FILES.

来源 (upload.php):

echo "<pre>";
print_r ($_FILES);
echo "</pre>";

来源 (index.html):

<!DOCTYPE html>
<html>
<head>
<title>Drag and Drop</title>
<style>
body {
    background: rgba(211,211,100, .5);
    font: 20px Arial;
}

.dropzone {
    width: 300px;
    height: 300px;
    border: 2px dashed #aaa;
    color: #aaa;
    line-height: 280px;
    text-align: center;
    position: absolute;
    left: 50%;
    margin-left: -150px;
    top: 50%;
    margin-top: -150px;
}

.dropzone.dragover {
    color: green;
    border: 2px dashed #000;
}
</style>
</head>
<body>
<p>Loaded files:</p>
<div id="uploads">
    <ul>

    </ul>
</div>
<div class="dropzone" id="dropzone">Drop files</div>
<script>

(function() {
    var formData = new FormData();
    var dropzone = document.getElementById("dropzone");

    dropzone.ondrop = function(e) {
        this.className = 'dropzone';
        this.innerHTML = 'Drop files';
        e.preventDefault();
        upload(e.dataTransfer.items);
    };

    function traverseFileTree(item, path) {
        path = path || "";
        if (item.isFile) {
            item.file(function(file) {
                console.log(file);                  // show info
                formData.append('file[]', file);    // file exist, but don't append
            });
        } /*else if (item.isDirectory) {
            var dirReader = item.createReader();
            dirReader.readEntries(function(entries) {
                for (var i=0; i<entries.length; i++) {
                    traverseFileTree(entries[i], path + item.name + "/");
                }
            });
        }*/
    } 


    var upload = function(items) {
        var xhr = new XMLHttpRequest();

        for(var i = 0; i < items.length; i++) {
            var item = items[i].webkitGetAsEntry();
            if (item) {
                traverseFileTree(item,'');
            }
        }

        xhr.onload = function() {
            console.log(this.responseText);
        };

        xhr.open('post', 'upload.php');
        xhr.send(formData);
    };

    dropzone.ondragover = function() {
        this.className = 'dropzone dragover';
        this.innerHTML = 'Mouse up';
        return false;
    };

    dropzone.ondragleave = function() {
        this.className = 'dropzone';
        this.innerHTML = 'Drop files';
        return false;
    };

})();
</script>

file()readEntries() return 的结果都是异步的。由于无法确定有多少文件或目录(它们本身可能包含包含更多文件或文件夹的其他目录)将被用户选择和删除,因此对 traverseFileTree 的单个或递归调用需要某种机制确定所有异步操作何时完成。这可以使用多种方法中的一种或多种来实现。

目前的方法递增一个变量 n,将每个单独的文件推送到一个数组 uploads。如果n0,处理第一个文件;递增 n 使其值 1 大于包含文件的数组 .length 直到数组 .length 等于 n - 1

  uploads.length === n - 1 || n === 0

然后使用 .slice() 复制 uploads 数组,将 uploads.lengthn 设置为 0,将文件数组传递给函数 processFiles在文件附加到 FormData() 对象的地方,调用 XMLHttpRequest()

<!DOCTYPE html>
<html>

<head>
  <title>Drag and Drop</title>
  <style>
    body {
      background: rgba(211, 211, 100, .5);
      font: 20px Arial;
    }

    .dropzone {
      width: 300px;
      height: 300px;
      border: 2px dashed #aaa;
      color: #aaa;
      line-height: 280px;
      text-align: center;
      position: absolute;
      left: 50%;
      margin-left: -150px;
      top: 50%;
      margin-top: -150px;
    }

    .dropzone.dragover {
      color: green;
      border: 2px dashed #000;
    }
  </style>
</head>

<body>
  <p>Loaded files:</p>
  <div id="uploads">
    <ul>

    </ul>
  </div>
  <div class="dropzone" id="dropzone">Drop files</div>
  <script>
    (function() {
      var n = 0, uploads = [];

      var dropzone = document.getElementById("dropzone");

      dropzone.ondrop = function(e) {
        this.className = 'dropzone';
        this.innerHTML = 'Drop files';
        e.preventDefault();
        upload(e.dataTransfer.items);

      };

      function processFiles(files) {
        console.log("files:", files);
        alert("processing " + files.length + " files");
        var formData = new FormData();
        // append files to `formData`
        for (file of files) {
          formData.append("file[]", file, file.name)
        }
        // check `formData` entries
        var curr = 0;
        for (data of formData.entries()) {
          console.log("formData entry " + curr, data);
          ++curr;
        }
        delete curr;
        // do ajax stuff here
        var xhr = new XMLHttpRequest();
        xhr.onload = function() {
        console.log(this.responseText);
        };

        xhr.open("POST", "upload.php");
        xhr.send(formData);
      }

      function traverseFileTree(item, path) {

        var handleFiles = function handleFiles(item, path) {
          path = path || "";
          if (item.isFile) {
            item.file(function(file) {
              uploads.push(file); 
              console.log(file, n, uploads.length); // show info
              if (uploads.length === n - 1 || n === 0) {
                alert("traverseFiles complete, uploads length: " 
                      + uploads.length);
                var files = uploads.slice(0);
                n = uploads.length = 0;
                processFiles(files)
              } 
            });
          } else if (item.isDirectory) {
            var dirReader = item.createReader();
            dirReader.readEntries(function(entries) {
              // increment `n` here
              n += entries.length;
              for (var i = 0; i < entries.length; i++) {
                handleFiles(entries[i], path + item.name + "/");
              }
            });
          }
        }

        handleFiles(item, path);

      }

      var upload = function(items) {

        if (n !== 0 && uploads.length !== 0) {
          n = uploads.length = 0;
        }

        for (var i = 0; i < items.length; i++) {
          var item = items[i].webkitGetAsEntry();
          if (item) {
            traverseFileTree(item, "");
          }
        }

      };

      dropzone.ondragover = function() {
        this.className = 'dropzone dragover';
        this.innerHTML = 'Mouse up';
        return false;
      };

      dropzone.ondragleave = function() {
        this.className = 'dropzone';
        this.innerHTML = 'Drop files';
        return false;
      };

    })();
  </script>
</body>
</html>

plnkrhttp://plnkr.co/edit/OdFrwYH2gmbtvHfq3ZjH?p=preview


另见 ,