3

I want to upload folder to server by d'n'd via AJAX. But already, I have troubles with upload files.

I use e.dataTransfer.items and webkitGetAsEntry() to check - is it file or folder?

If it's file, in function traverseFileTree I get file, but I can't append it to formData.

If I use e.dataTransfer.files, I don't know what is it. File or Folder because webkitGetAsEntry() get error.

What I do wrong? How transfer files to global array $_FILES.

Source (upload.php):

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

Source (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>

  • so, you only want this to work on chrome and other webkit based browsers? – Jaromanda X Sep 06 '16 at 11:22
  • @JaromandaX firefox supports folder upload, see [Select & Drop Files and/or Folders to be parsed](http://stackoverflow.com/questions/36842425/select-drop-files-and-or-folders-to-be-parsed/) – guest271314 Sep 06 '16 at 11:29
  • @JaromandaX yes, I want it work in chrome....I don't care. I just want it work. I can't resolve it some days, mybe week. I want do somethink like in dropBox. There is user can drop folder. – Серафим Sep 06 '16 at 11:30
  • @Серафим See [How can I filter out directories from upload handler in Firefox?](http://stackoverflow.com/questions/37443968/how-can-i-filter-out-directories-from-upload-handler-in-firefox/) , [How to read files from folder](http://stackoverflow.com/questions/36826601/how-to-read-files-from-folder/) – guest271314 Sep 06 '16 at 11:36
  • @guest271314 I do so [link](http://stackoverflow.com/questions/36826601/how-to-read-files-from-folder/) but, I just can't append file to `formData`. Why? I don't know. But in console writing all information about file, in `formData` - nothing. – Серафим Sep 06 '16 at 12:00

1 Answers1

2

Both file() and readEntries() return results asynchronously. Since it is impossible to know for certain how many files or directories, which themselves could contain additional directories containing still more files or folders, will be selected and dropped by user, the a single or recursive calls to traverseFileTree require some mechanism to determine when all asynchronous operations have completed. This can be achieved using one or more of several approaches.

The present approach increments a variable n, pushes each individual file to an array uploads. If n is 0, process the first file; increment n so that its value 1 greater than the array containing files .length until the array .length is equal to n - 1

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

then copy uploads array using .slice(), set uploads.length and n to 0, pass array of files to function processFiles where files are appended to FormData() object, call to XMLHttpRequest() is made.

<!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>

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


See also How can I filter out directories from upload handler in Firefox? , How to read files from folder

Community
  • 1
  • 1
guest271314
  • 1
  • 15
  • 104
  • 177
  • 1
    Thank you very much! It's usefull and workable However, I have some unclear point. Maybe eventually I understand why so. I'm sorry I did not answer for so long and thanks for the detailed answer! Slightly changed the code, here is the result: [codepen.io](http://codepen.io/work_forfood/pen/bwVWXL) – Серафим Sep 10 '16 at 06:10