javascript - HTML5 ondrop event returns before zip.js can finish operations -


the crux of issue need use datatransferitemlist asynchronously @ odds functionality described in specs, locked out of datatransfer.items collection once event ends.

https://bugs.chromium.org/p/chromium/issues/detail?id=137231 http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-data-store

the case offender following. more detailed description of problem , thoughts below it.

drophandler: function(event) {     event.stoppropagation();     event.preventdefault();     event.datatransfer.dropeffect = 'copy';     zip.workerscriptspath = "../bower_components/zip.js/webcontent/";     zip.usewebworkers = false; // disabled because makes life more complicated     // check if files contains zip     if (event.datatransfer.files[0].name.match(/(?:\.([^.]+))?$/) == 'zip') {         var reader = new filereader();         = this;         reader.onload = function(e) {             that.fire('zipuploaded', e.target.result.split(',')[1]);         }         reader.readasdataurl(event.datatransfer.files[0]);         // rev in browser zipping     } else {         var = this;         var items = event.datatransfer.items;         // async operation, execution falls through here         zip.createwriter(new zip.data64uriwriter(), function(writer) {             (function traverse(list, path, i, depth) {                 return new promise(function(resolve, reject) {                     var item;                     if (depth == 0) {                         if (i == list.length) {                             writer.close(function(uri) {                                 that.fire('zipuploaded', uri.split(',')[1]); // base64, please                                 fulfill(1);                                 return;                             });                         } else {                             console.log(i);                             console.log(list);                             var item = list[i].webkitgetasentry();                         }                     } else {                         if (i == list.length) {                             resolve(0);                             return;                         } else {                             item = list[i];                         }                     }                     if (item.isfile) {                         item.file(function(file) {                             // zipping operations done asynchronously, it'll fail second operation                             writer.add(path + file.name, zip.blobreader(file), function() {                                 traverse(list, path, + 1, depth).then(resolve(0)); // next item                             });                         });                     } else if (item.isdirectory) {                         var dirreader = item.createdirreader();                         dirreader.readentries(function(entries) {                             // operate on child folder next item @ level                             traverse(entries, path + item.name + "/", 0, depth + 1).then(function() {                                 traverse(list, path, + 1, depth).then(resolve(0));                             });                         });                     }                 });             })(items, "", 0, 0); // begin datatransferitemlist, 0th element, depth 0         });         this.$.uploadarea.classlist.remove('highlightdrag');     }     // when exit kills event.datatransfer.items }, 

i using zip.js asynchronous html5 dnd api. ondrop event ends before asynchronous zip.createwriter/writer.add operations finish. can think of 4 ways solve although don't know how implement of them , advice.

  1. block until createwriter done. (blocking javascript? uhoh)
  2. prevent ondrop locking me out of datatransfer.items (it seems security unlikely)
  3. synchronously copy out contents of datatransfer.items first (probably slow)
  4. do synchronously (don't think zip.js allows this, jszip does, moved away due having own limitations large file sets)

html5 dnd works expected. problem is, when adding multiple files, if add file before previous finish, zip.js breaks silently. can fixed calling writer.add in series.

the snippet might not work, see pen instead.

this example flats structure of dropped files, add zip in series.

function mes(it) {    const m = document.queryselector('#mes')    return m.textcontent = + '\n' + m.textcontent  }    function read(items) {    return promise.all(items.map(item => {      if (item.isfile) return [item]      return new promise(resolve => item.createreader().readentries(resolve))      .then(entries => {        entries.foreach(it => it.path = item.path + '/' + it.name)        return read(entries)      })    })).then(entries => entries.reduce((a, b) => a.concat(b)))  }    function handleresult(blob){    const res = document.queryselector('#result')    res.download = 'files.zip'    res.href = window.url.createobjecturl(blob)    res.textcontent = 'download zipped file'  }    function handleitems(items){    mes(items.length)    items.foreach(item => item.path = item.name)    const initzip = new promise(resolve =>      zip.createwriter(new zip.blobwriter, resolve)    )    const getfiles = read(items).then(entries => {      return promise.all(entries.map(entry =>        new promise(resolve =>          entry.file(file => {            file.path = entry.path            resolve(file)          })        )      ))    })    return promise.all([getfiles, initzip]).then(([files, writer]) =>      files.reduce((current, next) =>        current.then(() =>          new promise(resolve => {            mes(next.path)            writer.add(next.path, new zip.blobreader(next), resolve)          })        )      , promise.resolve())      .then(() => writer.close(handleresult))    )  }    zip.usewebworkers = false  const drop = document.queryselector('#drop');    ['dragover', 'drop'].foreach(name =>    drop.addeventlistener(name, ev => ev.preventdefault())  )  drop.addeventlistener('drop', ev => {    const items = [].slice.call(ev.datatransfer.items)    .map(item => item.webkitgetasentry())    return handleitems(items)  })
html, body, #drop {    height: 100%;    width: 100%;  }
<script src="http://gildas-lormeau.github.io/zip.js/demos/zip.js"></script>  <script src="http://gildas-lormeau.github.io/zip.js/demos/deflate.js"></script>      <div id="drop">    drop here!    <br>    <a id="result"></a>  </div>  <pre id="mes"></pre>

jszip easier this, might want give try.


Comments

Popular posts from this blog

java - Jasper subreport showing only one entry from the JSON data source when embedded in the Title band -

serialization - Convert Any type in scala to Array[Byte] and back -

SonarQube Plugin for Jenkins does not find SonarQube Scanner executable -