我还没有看到执行此操作的示例。API规范中不允许这样做吗?
我正在寻找一种简单的拖放解决方案,用于上传整个文件夹的照片树。
我还没有看到执行此操作的示例。API规范中不允许这样做吗?
我正在寻找一种简单的拖放解决方案,用于上传整个文件夹的照片树。
Answers:
多亏了Chrome> = 21,现在才有可能。
function traverseFileTree(item, path) {
path = path || "";
if (item.isFile) {
// Get file
item.file(function(file) {
console.log("File:", path + file.name);
});
} else if (item.isDirectory) {
// Get folder contents
var dirReader = item.createReader();
dirReader.readEntries(function(entries) {
for (var i=0; i<entries.length; i++) {
traverseFileTree(entries[i], path + item.name + "/");
}
});
}
}
dropArea.addEventListener("drop", function(event) {
event.preventDefault();
var items = event.dataTransfer.items;
for (var i=0; i<items.length; i++) {
// webkitGetAsEntry is where the magic happens
var item = items[i].webkitGetAsEntry();
if (item) {
traverseFileTree(item);
}
}
}, false);
更多信息:https://protonet.info/blog/html5-experiment-drag-drop-of-folders/
readEntries
不会将所有内容返回目录中。根据您提供的错误链接,我写了一个完整的答案:stackoverflow.com/a/53058574/885922
不幸的是,现有答案中没有一个是完全正确的,因为readEntries
不一定会返回给定目录的所有(文件或目录)条目。这是API规范的一部分(请参见下面的文档部分)。
为了真正获取所有文件,我们需要readEntries
重复调用(对于遇到的每个目录),直到它返回一个空数组。否则,我们将丢失目录中的某些文件/子目录(例如,在Chrome中),readEntries
一次最多只能返回100个条目。
使用Promises(await
/ async
)可以更清楚地演示readEntries
(因为它是异步的)的正确用法,并使用广度优先搜索(BFS)遍历目录结构:
// Drop handler function to get all files
async function getAllFileEntries(dataTransferItemList) {
let fileEntries = [];
// Use BFS to traverse entire directory/file structure
let queue = [];
// Unfortunately dataTransferItemList is not iterable i.e. no forEach
for (let i = 0; i < dataTransferItemList.length; i++) {
queue.push(dataTransferItemList[i].webkitGetAsEntry());
}
while (queue.length > 0) {
let entry = queue.shift();
if (entry.isFile) {
fileEntries.push(entry);
} else if (entry.isDirectory) {
queue.push(...await readAllDirectoryEntries(entry.createReader()));
}
}
return fileEntries;
}
// Get all the entries (files or sub-directories) in a directory
// by calling readEntries until it returns empty array
async function readAllDirectoryEntries(directoryReader) {
let entries = [];
let readEntries = await readEntriesPromise(directoryReader);
while (readEntries.length > 0) {
entries.push(...readEntries);
readEntries = await readEntriesPromise(directoryReader);
}
return entries;
}
// Wrap readEntries in a promise to make working with readEntries easier
// readEntries will return only some of the entries in a directory
// e.g. Chrome returns at most 100 entries at a time
async function readEntriesPromise(directoryReader) {
try {
return await new Promise((resolve, reject) => {
directoryReader.readEntries(resolve, reject);
});
} catch (err) {
console.log(err);
}
}
Codepen上的完整工作示例:https ://codepen.io/anon/pen/gBJrOP
FWIW我之所以选择它,是因为使用接受的答案时,我没有找回包含40,000个文件的目录(许多目录包含100个以上的文件/子目录)的所有文件。
说明文件:
FileSystemDirectoryReader中记录了此行为。重点摘录:
readEntries()
返回一个包含一些 目录条目的数组。数组中的每个项目都是基于FileSystemEntry的对象,通常是FileSystemFileEntry或FileSystemDirectoryEntry。
但公平地说,MDN文档可以在其他部分中更清楚地说明这一点。该readEntries()文件只是指出:
readEntries()方法检索正在读取的目录中的目录条目,并将它们以数组形式传递给提供的回调函数
并且唯一需要多次调用的提示是在successCallback参数的描述中:
如果没有剩余文件,或者您已经在此FileSystemDirectoryReader上调用了readEntries(),则该数组为空。
可以说,API也可能更直观,但是如文档所述:它是非标准/实验性功能,不在标准轨道上,并且不能指望它适用于所有浏览器。
有关:
readEntries
一个目录(验证为Chrome 64)最多返回100个条目。readEntries
很好地解释了此答案的正确用法(尽管没有代码)。readEntries
在没有BFS的情况下以异步方式正确调用。他还指出,Firefox返回目录中的所有条目(与Chrome不同),但是给定规范,我们不能依靠它。FileSystemFileEntry
为。如果您还需要完整的路径,则应使用(将仅是名称)。File
file(successCb, failureCb)
fileEntry.fullPath
file.webkitRelativePath
此功能将为您提供所有删除文件的数组的保证,例如<input type="file"/>.files
:
function getFilesWebkitDataTransferItems(dataTransferItems) {
function traverseFileTreePromise(item, path='') {
return new Promise( resolve => {
if (item.isFile) {
item.file(file => {
file.filepath = path + file.name //save full path
files.push(file)
resolve(file)
})
} else if (item.isDirectory) {
let dirReader = item.createReader()
dirReader.readEntries(entries => {
let entriesPromises = []
for (let entr of entries)
entriesPromises.push(traverseFileTreePromise(entr, path + item.name + "/"))
resolve(Promise.all(entriesPromises))
})
}
})
}
let files = []
return new Promise((resolve, reject) => {
let entriesPromises = []
for (let it of dataTransferItems)
entriesPromises.push(traverseFileTreePromise(it.webkitGetAsEntry()))
Promise.all(entriesPromises)
.then(entries => {
//console.log(entries)
resolve(files)
})
})
}
dropArea.addEventListener("drop", function(event) {
event.preventDefault();
var items = event.dataTransfer.items;
getFilesFromWebkitDataTransferItems(items)
.then(files => {
...
})
}, false);
https://www.npmjs.com/package/datatransfer-files-promise
用法示例:https: //github.com/grabantot/datatransfer-files-promise/blob/master/index.html
function getFilesWebkitDataTransferItems(dataTransfer)
应该是function getFilesWebkitDataTransferItems(items)
,for (entr of entries)
应该是for (let entr of entries)
。
readEntries
重复调用,直到返回一个空数组。
在发给HTML 5邮件列表的消息中,伊恩·希克森(Ian Hickson)说:
HTML5现在必须一次上传许多文件。浏览器可以允许用户一次选择多个文件,包括跨多个目录;这超出了规范的范围。
(另请参见原始功能建议。)因此可以安全地假设他考虑使用拖放功能上传文件夹也超出范围。显然,这取决于浏览器来提供单个文件。
如Lars Gunther所述,上传文件夹还存在其他一些困难:
此[…]提议必须进行两项检查(如果完全可行):
最大大小,可阻止某人上传包含数百个未压缩原始图像的完整目录...
即使忽略accept属性,也要进行过滤。Mac OS元数据和Windows缩略图等应省略。默认情况下,所有隐藏文件和目录均应排除。
现在,您可以通过拖放和输入来上传目录。
<input type='file' webkitdirectory >
以及拖放(对于Webkit浏览器)。
处理拖放文件夹。
<div id="dropzone"></div>
<script>
var dropzone = document.getElementById('dropzone');
dropzone.ondrop = function(e) {
var length = e.dataTransfer.items.length;
for (var i = 0; i < length; i++) {
var entry = e.dataTransfer.items[i].webkitGetAsEntry();
if (entry.isFile) {
... // do whatever you want
} else if (entry.isDirectory) {
... // do whatever you want
}
}
};
</script>
资源:
http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available
自2016年11月15日起,Firefox现在支持在v50.0中进行文件夹上传:https : //developer.mozilla.org/zh-CN/Firefox/Releases/50#Files_and_directories
您可以将文件夹拖放到Firefox中,也可以浏览并选择要上传的本地文件夹。它还支持嵌套在子文件夹中的文件夹。
这意味着您现在可以使用Chrome,Firefox,Edge或Opera上载文件夹。您目前无法使用Safari或Internet Explorer。
这是有关如何使用文件和目录条目API的完整示例:
var dropzone = document.getElementById("dropzone");
var listing = document.getElementById("listing");
function scanAndLogFiles(item, container) {
var elem = document.createElement("li");
elem.innerHTML = item.name;
container.appendChild(elem);
if (item.isDirectory) {
var directoryReader = item.createReader();
var directoryContainer = document.createElement("ul");
container.appendChild(directoryContainer);
directoryReader.readEntries(function(entries) {
entries.forEach(function(entry) {
scanAndLogFiles(entry, directoryContainer);
});
});
}
}
dropzone.addEventListener(
"dragover",
function(event) {
event.preventDefault();
},
false
);
dropzone.addEventListener(
"drop",
function(event) {
var items = event.dataTransfer.items;
event.preventDefault();
listing.innerHTML = "";
for (var i = 0; i < items.length; i++) {
var item = items[i].webkitGetAsEntry();
if (item) {
scanAndLogFiles(item, listing);
}
}
},
false
);
body {
font: 14px "Arial", sans-serif;
}
#dropzone {
text-align: center;
width: 300px;
height: 100px;
margin: 10px;
padding: 10px;
border: 4px dashed red;
border-radius: 10px;
}
#boxtitle {
display: table-cell;
vertical-align: middle;
text-align: center;
color: black;
font: bold 2em "Arial", sans-serif;
width: 300px;
height: 100px;
}
<p>Drag files and/or directories to the box below!</p>
<div id="dropzone">
<div id="boxtitle">
Drop Files Here
</div>
</div>
<h2>Directory tree:</h2>
<ul id="listing"></ul>
webkitGetAsEntry
Chrome 13 +,Firefox 50+和Edge支持。
来源:https : //developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
HTML5是否允许拖放拖放文件夹或文件夹树?
仅Chrome支持此功能。它没有任何牵引力,很可能会被移除。
参考:https : //developer.mozilla.org/en/docs/Web/API/DirectoryReader#readEntries
readEntries
如果readEntries
仍在运行另一个调用,则无法调用他已解决/实现的问题。DirectoryReader API设计不是最好的
最近偶然发现需要在我的两个项目中实现此功能,因此我创建了许多实用程序函数来帮助实现此目的。
创建一个表示所有文件夹,文件及其之间关系的数据结构,如下所示:
{
folders: [
{
name: string,
folders: Array,
files: Array
},
/* ... */
],
files: Array
}
而另一个仅返回所有文件的数组(在所有文件夹和子文件夹中)。
这是软件包的链接:https : //www.npmjs.com/package/file-system-utils
input type=file
:stackoverflow.com/questions/9518335/...