Ich habe keine Beispiele dafür gesehen. Ist dies in der API-Spezifikation nicht zulässig?
Ich suche nach einer einfachen Drag-Drop-Lösung zum Hochladen eines gesamten Ordnerbaums mit Fotos.
Ich habe keine Beispiele dafür gesehen. Ist dies in der API-Spezifikation nicht zulässig?
Ich suche nach einer einfachen Drag-Drop-Lösung zum Hochladen eines gesamten Ordnerbaums mit Fotos.
Antworten:
Dank Chrome> = 21 ist dies jetzt möglich.
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);
Weitere Informationen: https://protonet.info/blog/html5-experiment-drag-drop-of-folders/
readEntries
dass nicht alle Einträge in einem Verzeichnis zurückgegeben werden. Basierend auf dem von Ihnen bereitgestellten Fehlerlink
Leider ist keine der vorhandenen Antworten vollständig korrekt, da readEntries
nicht unbedingt ALLE (Datei- oder Verzeichnis-) Einträge für ein bestimmtes Verzeichnis zurückgegeben werden. Dies ist Teil der API-Spezifikation (siehe Abschnitt Dokumentation unten).
Um tatsächlich alle Dateien zu erhalten, müssen wir readEntries
wiederholt (für jedes Verzeichnis, auf das wir stoßen) aufrufen, bis ein leeres Array zurückgegeben wird. Wenn wir dies nicht tun, werden wir einige Dateien / Unterverzeichnisse in einem Verzeichnis vermissen, z. B. in Chrome.readEntries
werden höchstens 100 Einträge gleichzeitig zurückgegeben.
Verwenden von Promises ( await
/ async
), um die korrekte Verwendung von readEntries
(da es asynchron ist) und Breitensuche (BFS) zum Durchlaufen der Verzeichnisstruktur deutlicher zu demonstrieren :
// 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);
}
}
Vollständiges Arbeitsbeispiel für Codepen: https://codepen.io/anon/pen/gBJrOP
FWIW Ich habe dies nur aufgegriffen, weil ich bei Verwendung der akzeptierten Antwort nicht alle erwarteten Dateien in einem Verzeichnis mit 40.000 Dateien (viele Verzeichnisse mit weit über 100 Dateien / Unterverzeichnissen) zurückerhalten habe.
Dokumentation:
Dieses Verhalten ist in FileSystemDirectoryReader dokumentiert . Auszug mit Hervorhebung hinzugefügt:
readEntries ()
Gibt ein Array zurück, das einige Einträge des Verzeichnisses enthält . Jedes Element im Array ist ein Objekt, das auf FileSystemEntry basiert - normalerweise entweder FileSystemFileEntry oder FileSystemDirectoryEntry.
Um fair zu sein, könnte die MDN-Dokumentation dies in anderen Abschnitten klarer machen. In der Dokumentation zu readEntries () wird lediglich Folgendes angegeben :
Die Methode readEntries () ruft die Verzeichniseinträge im zu lesenden Verzeichnis ab und liefert sie in einem Array an die bereitgestellte Rückruffunktion
Die einzige Erwähnung / Andeutung, dass mehrere Aufrufe erforderlich sind, ist die Beschreibung des Parameters successCallback :
Wenn in diesem FileSystemDirectoryReader keine Dateien mehr vorhanden sind oder Sie readEntries () bereits aufgerufen haben, ist das Array leer.
Möglicherweise könnte die API auch intuitiver sein, aber wie in der Dokumentation angegeben: Es handelt sich um eine nicht standardmäßige / experimentelle Funktion, die sich nicht auf einer Standardspur befindet und nicht für alle Browser geeignet ist.
Verbunden:
readEntries
höchstens 100 Einträge für ein Verzeichnis zurückgegeben werden (verifiziert als Chrome 64).readEntries
ziemlich gut in dieser Antwort (wenn auch ohne Code).readEntries
ohne BFS asynchron korrekt aufgerufen . Er stellt außerdem fest, dass Firefox alle Einträge in einem Verzeichnis zurückgibt (im Gegensatz zu Chrome), aber wir können uns angesichts der Spezifikation nicht darauf verlassen.FileSystemFileEntry
um File
über das file(successCb, failureCb)
Verfahren. Wenn Sie auch den vollständigen Pfad benötigen, sollten Sie diesen von übernehmen fileEntry.fullPath
( file.webkitRelativePath
wird nur der Name sein).
Diese Funktion gibt Ihnen ein Versprechen für das Array aller abgelegten Dateien, wie <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
Anwendungsbeispiel: https://github.com/grabantot/datatransfer-files-promise/blob/master/index.html
function getFilesWebkitDataTransferItems(dataTransfer)
sollte sein function getFilesWebkitDataTransferItems(items)
und for (entr of entries)
sollte sein for (let entr of entries)
.
readEntries
wiederholt aufgerufen werden muss, bis ein leeres Array zurückgegeben wird.
In dieser Nachricht an die HTML 5-Mailingliste sagt Ian Hickson:
HTML5 muss jetzt viele Dateien gleichzeitig hochladen. Mit Browsern können Benutzer mehrere Dateien gleichzeitig auswählen, auch in mehreren Verzeichnissen. Das liegt etwas außerhalb des Bereichs der Spezifikation.
(Siehe auch den ursprünglichen Funktionsvorschlag .) Man kann also davon ausgehen, dass er das Hochladen von Ordnern per Drag & Drop auch außerhalb des Gültigkeitsbereichs in Betracht zieht. Anscheinend liegt es am Browser, einzelne Dateien bereitzustellen.
Das Hochladen von Ordnern hätte auch einige andere Schwierigkeiten, wie von Lars Gunther beschrieben :
Dieser […] Vorschlag muss zwei Prüfungen enthalten (sofern überhaupt möglich):
Maximale Größe, um zu verhindern, dass jemand ein vollständiges Verzeichnis mit mehreren hundert unkomprimierten Rohbildern hochlädt ...
Filtern, auch wenn das Attribut accept weggelassen wird. Mac OS-Metadaten und Windows-Miniaturansichten usw. sollten weggelassen werden. Alle versteckten Dateien und Verzeichnisse sollten standardmäßig ausgeschlossen werden.
Jetzt können Sie Verzeichnisse per Drag & Drop und Eingabe hochladen.
<input type='file' webkitdirectory >
und für Drag & Drop (für Webkit-Browser).
Umgang mit Drag & Drop-Ordnern.
<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>
Ressourcen:
http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available
Firefox unterstützt jetzt das Hochladen von Ordnern ab dem 15. November 2016 in Version 50.0: https://developer.mozilla.org/en-US/Firefox/Releases/50#Files_and_directories
Sie können Ordner in Firefox ziehen und ablegen oder einen lokalen Ordner zum Hochladen durchsuchen und auswählen. Es werden auch Ordner unterstützt, die in Unterordnern verschachtelt sind.
Das bedeutet, dass Sie jetzt entweder Chrome, Firefox, Edge oder Opera verwenden können, um Ordner hochzuladen. Sie können derzeit weder Safari noch Internet Explorer verwenden.
Hier ist ein vollständiges Beispiel für die Verwendung der API für Datei- und Verzeichniseinträge :
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
wird von Chrome 13+, Firefox 50+ und Edge unterstützt.
Quelle: https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
Ermöglicht HTML5 das Hochladen von Ordnern oder eines Ordnerbaums per Drag & Drop?
Nur Chrome unterstützt diese Funktion. Es hat keine Traktion und wird wahrscheinlich entfernt.
Ref: https://developer.mozilla.org/en/docs/Web/API/DirectoryReader#readEntries
readEntries
kann nicht aufgerufen werden, wenn noch ein weiterer Aufruf von ausgeführt readEntries
wird. Das DirectoryReader-API-Design ist nicht das beste
UPDATE: Seit 2012 hat sich viel geändert, siehe stattdessen die obigen Antworten. Ich lasse diese Antwort hier der Archäologie zuliebe.
Die HTML5-Spezifikation besagt NICHT, dass der Browser bei der Auswahl eines Ordners zum Hochladen alle enthaltenen Dateien rekursiv hochladen sollte.
In Chrome / Chromium können Sie zwar einen Ordner hochladen, aber wenn Sie dies tun, wird nur eine bedeutungslose 4-KB-Datei hochgeladen, die das Verzeichnis darstellt. Einige serverseitige Anwendungen wie Alfresco können dies erkennen und den Benutzer warnen, dass Ordner nicht hochgeladen werden können:
Ich bin kürzlich auf die Notwendigkeit gestoßen, dies in zwei meiner Projekte zu implementieren, und habe daher eine Reihe von Dienstprogrammfunktionen erstellt, um dies zu unterstützen.
Man erstellt eine Datenstruktur, die alle Ordner, Dateien und Beziehungen zwischen ihnen darstellt, wie so 👇
{
folders: [
{
name: string,
folders: Array,
files: Array
},
/* ... */
],
files: Array
}
Während der andere nur ein Array aller Dateien zurückgibt (in allen Ordnern und Unterordnern).
Hier ist der Link zum Paket: https://www.npmjs.com/package/file-system-utils
input type=file
: stackoverflow.com/questions/9518335/…