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.
html
file-upload
drag-and-drop
Michael
quelle
quelle
input type=file
: stackoverflow.com/questions/9518335/…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/
quelle
readEntries
dass nicht alle Einträge in einem Verzeichnis zurückgegeben werden. Basierend auf dem von Ihnen bereitgestellten FehlerlinkLeider 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 vonreadEntries
(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:
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 einzige Erwähnung / Andeutung, dass mehrere Aufrufe erforderlich sind, ist die Beschreibung des Parameters successCallback :
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.quelle
FileSystemFileEntry
umFile
über dasfile(successCb, failureCb)
Verfahren. Wenn Sie auch den vollständigen Pfad benötigen, sollten Sie diesen von übernehmenfileEntry.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) }) }) }
Verwendung:
dropArea.addEventListener("drop", function(event) { event.preventDefault(); var items = event.dataTransfer.items; getFilesFromWebkitDataTransferItems(items) .then(files => { ... }) }, false);
npm-Paket
https://www.npmjs.com/package/datatransfer-files-promise
Anwendungsbeispiel: https://github.com/grabantot/datatransfer-files-promise/blob/master/index.html
quelle
function getFilesWebkitDataTransferItems(dataTransfer)
sollte seinfunction getFilesWebkitDataTransferItems(items)
undfor (entr of entries)
sollte seinfor (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:
(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 :
quelle
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
quelle
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.
quelle
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
quelle
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
quelle
readEntries
kann nicht aufgerufen werden, wenn noch ein weiterer Aufruf von ausgeführtreadEntries
wird. Das DirectoryReader-API-Design ist nicht das besteUPDATE: 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:
quelle
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
quelle