HTML5 ondrop 事件 returns 在 zip.js 可以完成操作之前
HTML5 ondrop event returns before zip.js can finish operations
我的问题的症结在于我需要异步使用 datatransferitemlist,这与规范中描述的功能不一致,即事件结束后您将被锁定在 dataTransfer.items 集合之外.
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
案犯如下。下面对我的问题和想法进行了更详细的描述。
drophandler: function(event) {
event.stopPropagation();
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
zip.workerScriptsPath = "../bower_components/zip.js/WebContent/";
zip.useWebWorkers = false; // Disabled because it just makes life more complicated
// Check if files contains just a zip
if (event.dataTransfer.files[0].name.match(/(?:\.([^.]+))?$/) == 'zip') {
var reader = new FileReader();
that = this;
reader.onload = function(e) {
that.fire('zipuploaded', e.target.result.split(',')[1]);
}
reader.readAsDataURL(event.dataTransfer.files[0]);
// Rev up that in browser zipping
} else {
var that = this;
var items = event.dataTransfer.items;
// Async operation, execution falls through from 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]); // Just the 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 by roughly the second operation
writer.add(path + file.name, zip.BlobReader(file), function() {
traverse(list, path, i + 1, depth).then(resolve(0)); // Next item
});
});
} else if (item.isDirectory) {
var dirReader = item.createDirReader();
dirReader.readEntries(function(entries) {
// Operate on child folder then the next item at this level
traverse(entries, path + item.name + "/", 0, depth + 1).then(function() {
traverse(list, path, i + 1, depth).then(resolve(0));
});
});
}
});
})(items, "", 0, 0); // Begin with datatransferitemlist, 0th element, depth 0
});
this.$.uploadarea.classList.remove('highlightdrag');
}
// When we exit it kills the event.dataTransfer.items
},
我正在使用与 HTML5 DnD API 异步的 zip.js。
ondrop 事件在异步 zip.createWriter/writer.add 操作完成之前结束。我可以想出四种方法来解决这个问题,尽管我不知道如何实现它们中的任何一种,并希望得到一些建议。
- 阻塞直到 createWriter 完成。 (屏蔽javascript?呃)
- 防止 ondrop 将我锁定在 dataTransfer.items 之外(这似乎是为了安全不太可能)
- 先同步复制出dataTransfer.items的内容(可能很慢)
- 同步执行所有操作(不要认为 zip.js 允许这样做,JsZip 允许,但我放弃了它,因为它对大文件集有其自身的限制)
HTML5 DnD 按预期工作。问题是,添加多个文件时,如果您在上一次完成之前添加一个文件,zip.js
会静默中断。这可以通过连续调用 writer.add
来解决。
该代码段可能无效,请参阅此 pen。
此示例将拖放文件的结构扁平化,然后将其添加到 zip 系列。
function mes(it) {
const m = document.querySelector('#mes')
return m.textContent = it + '\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比这容易多了,你不妨试一试。
我的问题的症结在于我需要异步使用 datatransferitemlist,这与规范中描述的功能不一致,即事件结束后您将被锁定在 dataTransfer.items 集合之外.
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
案犯如下。下面对我的问题和想法进行了更详细的描述。
drophandler: function(event) {
event.stopPropagation();
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
zip.workerScriptsPath = "../bower_components/zip.js/WebContent/";
zip.useWebWorkers = false; // Disabled because it just makes life more complicated
// Check if files contains just a zip
if (event.dataTransfer.files[0].name.match(/(?:\.([^.]+))?$/) == 'zip') {
var reader = new FileReader();
that = this;
reader.onload = function(e) {
that.fire('zipuploaded', e.target.result.split(',')[1]);
}
reader.readAsDataURL(event.dataTransfer.files[0]);
// Rev up that in browser zipping
} else {
var that = this;
var items = event.dataTransfer.items;
// Async operation, execution falls through from 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]); // Just the 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 by roughly the second operation
writer.add(path + file.name, zip.BlobReader(file), function() {
traverse(list, path, i + 1, depth).then(resolve(0)); // Next item
});
});
} else if (item.isDirectory) {
var dirReader = item.createDirReader();
dirReader.readEntries(function(entries) {
// Operate on child folder then the next item at this level
traverse(entries, path + item.name + "/", 0, depth + 1).then(function() {
traverse(list, path, i + 1, depth).then(resolve(0));
});
});
}
});
})(items, "", 0, 0); // Begin with datatransferitemlist, 0th element, depth 0
});
this.$.uploadarea.classList.remove('highlightdrag');
}
// When we exit it kills the event.dataTransfer.items
},
我正在使用与 HTML5 DnD API 异步的 zip.js。 ondrop 事件在异步 zip.createWriter/writer.add 操作完成之前结束。我可以想出四种方法来解决这个问题,尽管我不知道如何实现它们中的任何一种,并希望得到一些建议。
- 阻塞直到 createWriter 完成。 (屏蔽javascript?呃)
- 防止 ondrop 将我锁定在 dataTransfer.items 之外(这似乎是为了安全不太可能)
- 先同步复制出dataTransfer.items的内容(可能很慢)
- 同步执行所有操作(不要认为 zip.js 允许这样做,JsZip 允许,但我放弃了它,因为它对大文件集有其自身的限制)
HTML5 DnD 按预期工作。问题是,添加多个文件时,如果您在上一次完成之前添加一个文件,zip.js
会静默中断。这可以通过连续调用 writer.add
来解决。
该代码段可能无效,请参阅此 pen。
此示例将拖放文件的结构扁平化,然后将其添加到 zip 系列。
function mes(it) {
const m = document.querySelector('#mes')
return m.textContent = it + '\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比这容易多了,你不妨试一试。