0

I'm trying to upload whole folders through the browser. I've done a fair bit of tinkering today and discovered that there are a number of great solutions for uploading folders. However, when testing them, they seem to be making the list of files in the folders ready for upload, sans directory structure.

Is there any recommended tool I can use to drag and drop, or even just select and upload, entire folders from my site (and of course, maintain structure)?

GeneralBear
  • 1,011
  • 3
  • 11
  • 35

1 Answers1

3

Not really officially yet, but there will probably be, available for us mere web-developers* through the Files and Directory API which specs are still in the process of being written, even though chrome and Firefox already implement something similar through a webkit-API*, which allows us to access and navigate dropped directories.

So if we try to use this webkit-API, we could write something like this:

/* constructs a simple directory view from a filesystem */
async function makedir(entries) {

  const systems = entries.map(entry => traverse(entry, {}));
  return Promise.all(systems);

  async function traverse(entry, fs) {
    if (entry.isDirectory) {
      fs[entry.name] = {};
      let dirReader = entry.createReader();
      await new Promise((res, rej) => {
        dirReader.readEntries(async entries => {
          for (let e of entries) {
            await traverse(e, fs[entry.name]);
          }
          res();
        }, rej);
      });
    } else if (entry.isFile) {
      await new Promise((res, rej) => {
        entry.file(file => {
          fs[entry.name] = file;
          res();
        }, rej);
      });
    }
    return fs;
  }
}

function readDropped(dT) {
  const entries = [...dT.items].map(item => {
      return item.webkitGetAsEntry ? item.webkitGetAsEntry() : null;
    })
    .filter(entry => entry);
  if (entries.length) {
    makedir(entries)
      .then(output)
      .catch(handleSecurityLimitation);
  } else notadir();

}

function notadir() {
  _log.textContent = "wasn't a directory, or webkitdirectory is not supported";
}

dropzone.ondragover = e => {
  e.preventDefault();
  dropzone.classList.add('over');
}
dropzone.ondragexit = dropzone.ondragend = e => dropzone.classList.remove('over');
dropzone.ondrop = e => {
  e.preventDefault();
  dropzone.classList.remove('over');
  readDropped(e.dataTransfer);
}

function output(system_trees) {
  console.log(system_trees);
  _log.textContent = JSON.stringify(system_trees, checkFile, 2);

  function checkFile(key, value) {
    if (value instanceof File) {
      return '{[File] ' + value.name + ', ' + value.size + 'b}';
    } else return value;
  }
}

function handleSecurityLimitation(error) {
  console.error(error);
  document.body.innerHTML = `
    <h2>Faced security limitations</h2>
    <a href="https://jsfiddle.net/x85vtnef/">Please go to this fiddle</a>`;
}
#dropzone {
  border: 1px solid;
  width: 90vw;
  height: 90vh;
  margin: 2vmin;
  padding: 2vmin;
  overflow: auto;
}

#dropzone.over {
  background-color: rgba(0, 0, 0, .2);
}
<div id="dropzone">
  Drop some directory here.
  <pre id="_log"></pre>
</div>

And as fiddle for chrome


*chrome code (extensions and plugins) generally already have access to a FileSystem API. *"webkit-API" here means experimental, i.e which behavior may change any time and may not be the same on every browsers.

Community
  • 1
  • 1
Kaiido
  • 123,334
  • 13
  • 219
  • 285