Nie widziałem żadnych przykładów, które to robią. Czy to nie jest dozwolone w specyfikacji interfejsu API?
Szukam prostego rozwiązania typu „przeciągnij i upuść” do przesłania całego drzewa folderów ze zdjęciami.
Nie widziałem żadnych przykładów, które to robią. Czy to nie jest dozwolone w specyfikacji interfejsu API?
Szukam prostego rozwiązania typu „przeciągnij i upuść” do przesłania całego drzewa folderów ze zdjęciami.
Odpowiedzi:
Teraz jest to możliwe dzięki 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);
Więcej informacji: https://protonet.info/blog/html5-experiment-drag-drop-of-folders/
readEntries
że nie zwróci wszystkich wpisów w katalogu. Na podstawie podanego przez Ciebie linku do błędu napisałem pełną odpowiedź: stackoverflow.com/a/53058574/885922
Niestety żadna z istniejących odpowiedzi nie jest całkowicie poprawna, ponieważ readEntries
niekoniecznie zwróci WSZYSTKIE wpisy (plik lub katalog) dla danego katalogu. Jest to część specyfikacji API (patrz sekcja Dokumentacja poniżej).
Aby faktycznie pobrać wszystkie pliki, będziemy musieli wywoływać readEntries
wielokrotnie (dla każdego napotkanego katalogu), dopóki nie zwróci pustej tablicy. Jeśli tego nie zrobimy, pominiemy niektóre pliki / podkatalogi w katalogu, np. W przeglądarce Chrome, zwrócimy readEntries
maksymalnie 100 wpisów na raz.
Używanie Promises ( await
/ async
) do wyraźniejszego zademonstrowania prawidłowego użycia readEntries
(ponieważ jest asynchroniczne) i przeszukiwania wszerz (BFS) do przechodzenia po strukturze katalogów:
// 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);
}
}
Kompletny przykład roboczy na Codepen: https://codepen.io/anon/pen/gBJrOP
FWIW Podniosłem to tylko dlatego, że nie odzyskałem wszystkich plików, których oczekiwałem w katalogu zawierającym 40 000 plików (wiele katalogów zawierających znacznie ponad 100 plików / podkatalogów), używając zaakceptowanej odpowiedzi.
Dokumentacja:
To zachowanie jest udokumentowane w FileSystemDirectoryReader . Fragment z dodanym naciskiem:
readEntries ()
Zwraca tablicę zawierającą pewną liczbę wpisów katalogu . Każdy element tablicy jest obiektem opartym na FileSystemEntry - zazwyczaj FileSystemFileEntry lub FileSystemDirectoryEntry.
Ale żeby być uczciwym, dokumentacja MDN mogłaby to wyjaśnić w innych sekcjach. Dokumentacja readEntries () po prostu zauważa:
Metoda readEntries () pobiera pozycje katalogu w czytanym katalogu i dostarcza je w tablicy do podanej funkcji zwrotnej
Jedyna wzmianka / wskazówka, że potrzeba wielu wywołań, znajduje się w opisie parametru successCallback :
Jeśli nie ma żadnych plików lub już wywołałeś readEntries () w tym FileSystemDirectoryReader, tablica jest pusta.
Zapewne API mogłoby być również bardziej intuicyjne, ale jak zauważa dokumentacja: jest to funkcja niestandardowa / eksperymentalna, nie na ścieżce standardów i nie można oczekiwać, że będzie działać we wszystkich przeglądarkach.
Związane z:
readEntries
zwróci maksymalnie 100 wpisów dla katalogu (zweryfikowanego jako Chrome 64).readEntries
dość dobrze wyjaśnia poprawne użycie w tej odpowiedzi (aczkolwiek bez kodu).readEntries
w sposób asynchroniczny bez BFS. Zauważa również, że Firefox zwraca wszystkie wpisy w katalogu (w przeciwieństwie do Chrome), ale nie możemy na tym polegać, biorąc pod uwagę specyfikację.FileSystemFileEntry
na File
, za pomocą file(successCb, failureCb)
metody. Jeśli potrzebujesz również pełnej ścieżki, powinieneś ją pobrać z fileEntry.fullPath
( file.webkitRelativePath
będzie tylko nazwa).
Ta funkcja daje obietnicę dotyczącą tablicy wszystkich upuszczonych plików, na przykład <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
przykład użycia: https://github.com/grabantot/datatransfer-files-promise/blob/master/index.html
function getFilesWebkitDataTransferItems(dataTransfer)
powinno być function getFilesWebkitDataTransferItems(items)
i for (entr of entries)
powinno być for (let entr of entries)
.
readEntries
wielokrotnego wywoływania, dopóki nie zwróci pustej tablicy.
W tej wiadomości do listy mailingowej HTML 5 Ian Hickson mówi:
HTML5 musi teraz przesyłać wiele plików naraz. Przeglądarki mogą pozwolić użytkownikom na wybieranie wielu plików jednocześnie, w tym w wielu katalogach; to trochę wykracza poza zakres specyfikacji.
(Zobacz także oryginalną propozycję funkcji ). Można więc bezpiecznie założyć, że rozważa przesyłanie folderów za pomocą metody przeciągania i upuszczania również poza zakresem. Najwyraźniej to przeglądarka musi obsługiwać poszczególne pliki.
Przesyłanie folderów wiązałoby się również z innymi trudnościami, które opisał Lars Gunther :
Ta […] propozycja musi mieć dwa sprawdzenia (jeśli w ogóle jest wykonalne):
Maksymalny rozmiar, aby uniemożliwić komuś załadowanie pełnego katalogu zawierającego kilkaset nieskompresowanych surowych obrazów ...
Filtrowanie, nawet jeśli pominięto atrybut accept. Należy pominąć metadane systemu Mac OS, miniatury systemu Windows itp. Wszystkie ukryte pliki i katalogi powinny być domyślnie wykluczone.
Teraz możesz przesyłać katalogi za pomocą przeciągania i upuszczania oraz wprowadzania.
<input type='file' webkitdirectory >
oraz do przeciągania i upuszczania (dla przeglądarek webkit).
Obsługa folderów typu „przeciągnij i upuść”.
<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>
Zasoby:
http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available
Firefox obsługuje teraz przesyłanie folderów od 15 listopada 2016 r. W wersji 50.0: https://developer.mozilla.org/en-US/Firefox/Releases/50#Files_and_directories
Możesz przeciągać i upuszczać foldery do przeglądarki Firefox lub przeglądać i wybierać folder lokalny do przesłania. Obsługuje również foldery zagnieżdżone w podfolderach.
Oznacza to, że możesz teraz przesyłać foldery za pomocą przeglądarki Chrome, Firefox, Edge lub Opera. Obecnie nie możesz używać Safari ani Internet Explorera.
Oto pełny przykład korzystania z interfejsu API plików i katalogów :
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
jest obsługiwany przez Chrome 13+, Firefox 50+ i Edge.
Źródło: https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
Czy HTML5 umożliwia przesyłanie folderów lub drzewa folderów metodą przeciągnij i upuść?
Tylko Chrome obsługuje tę funkcję. Nie miał żadnej przyczepności i prawdopodobnie zostanie usunięty.
Ref: https://developer.mozilla.org/en/docs/Web/API/DirectoryReader#readEntries
readEntries
nie może zostać wywołane, jeśli readEntries
nadal trwa inne wywołanie . Projekt interfejsu DirectoryReader API nie jest najlepszy
AKTUALIZACJA: Od 2012 roku wiele się zmieniło, zamiast tego zobacz odpowiedzi powyżej. Zostawiam tę odpowiedź tutaj ze względu na archeologię.
Specyfikacja HTML5 NIE mówi, że wybierając folder do przesłania, przeglądarka powinna przesyłać wszystkie zawarte w nim pliki rekurencyjnie.
Właściwie w Chrome / Chromium możesz przesłać folder, ale kiedy to zrobisz, po prostu przesyła bezsensowny plik 4KB, który reprezentuje katalog. Niektóre aplikacje po stronie serwera, takie jak Alfresco, mogą to wykryć i ostrzec użytkownika, że foldery nie mogą zostać przesłane:
Niedawno natknąłem się na potrzebę zaimplementowania tego w dwóch moich projektach, więc stworzyłem kilka funkcji narzędziowych, które pomogą w tym.
Tworzy się strukturę danych reprezentującą wszystkie foldery, pliki i relacje między nimi, w ten sposób 👇
{
folders: [
{
name: string,
folders: Array,
files: Array
},
/* ... */
],
files: Array
}
Podczas gdy drugi po prostu zwraca tablicę wszystkich plików (we wszystkich folderach i podfolderach).
Oto link do pakietu: https://www.npmjs.com/package/file-system-utils
input type=file
: stackoverflow.com/questions/9518335/…