Je n'ai vu aucun exemple qui le fasse. N'est-ce pas autorisé dans la spécification API?
Je recherche une solution simple de glisser-déposer pour télécharger une arborescence complète de dossiers de photos.
C'est désormais possible grâce à 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);
Plus d'informations: https://protonet.info/blog/html5-experiment-drag-drop-of-folders/
Malheureusement, aucune des réponses existantes n'est complètement correcte car readEntries
ne renverra pas nécessairement [~ # ~] tout [~ # ~] le (fichier ou répertoire) entrées pour un répertoire donné. Cela fait partie de la spécification API (voir la section Documentation ci-dessous).
Pour réellement récupérer tous les fichiers, nous devons appeler readEntries
à plusieurs reprises (pour chaque répertoire que nous rencontrons) jusqu'à ce qu'il renvoie un espace vide tableau. Si nous ne le faisons pas, nous manquerons certains fichiers/sous-répertoires dans un répertoire, par exemple dans Chrome, readEntries
ne renverra que 100 entrées à la fois.
Utiliser Promises (await
/async
) pour démontrer plus clairement l'utilisation correcte de readEntries
(car il est asynchrone) et BFS pour parcourir la structure du répertoire:
// 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);
}
}
Exemple de travail complet sur Codepen: https://codepen.io/anon/pen/gBJrOP
FWIW Je n'ai récupéré cela que parce que je ne récupérais pas tous les fichiers que j'attendais dans un répertoire contenant 40 000 fichiers (de nombreux répertoires contenant bien plus de 100 fichiers/sous-répertoires) lorsque j'utilisais la réponse acceptée.
Documentation:
Ce comportement est documenté dans FileSystemDirectoryReader . Extrait avec emphase ajoutée:
readEntries ()
Renvoie un tableau contenant un certain nombre de entrées du répertoire. Chaque élément du tableau est un objet basé sur FileSystemEntry, généralement FileSystemFileEntry ou FileSystemDirectoryEntry.
Mais pour être honnête, la documentation MDN pourrait rendre cela plus clair dans d'autres sections. La documentation readEntries () note simplement:
La méthode readEntries () récupère les entrées de répertoire dans le répertoire en cours de lecture et les remet dans un tableau à la fonction de rappel fournie
Et la seule mention/indication que plusieurs appels sont nécessaires se trouve dans la description du paramètre successCallback :
S'il ne reste aucun fichier ou si vous avez déjà appelé readEntries () sur ce FileSystemDirectoryReader, le tableau est vide.
On peut dire que l'API pourrait également être plus intuitive, mais comme le note la documentation: c'est une fonctionnalité non standard/expérimentale, pas sur une piste standard, et on ne peut pas s'attendre à ce qu'elle fonctionne pour tous les navigateurs.
Connexes:
readEntries
renverra au plus 100 entrées pour un répertoire (vérifié comme Chrome 64).readEntries
dans ce réponse (quoique sans code).readEntries
de manière asynchrone sans BFS. Il note également que Firefox renvoie toutes les entrées d'un répertoire (contrairement à Chrome) mais nous ne pouvons pas nous en remettre à cela compte tenu des spécifications.Dans ce message à la liste de diffusion HTML 5, Ian Hickson dit:
HTML5 doit maintenant télécharger de nombreux fichiers à la fois. Les navigateurs pourraient permettre aux utilisateurs de sélectionner plusieurs fichiers à la fois, y compris dans plusieurs répertoires; c'est un peu hors de portée de la spécification.
(Voir également l'original proposition de fonctionnalité .) Il est donc prudent de supposer qu'il considère le téléchargement de dossiers par glisser-déposer également hors de portée. Apparemment, c'est au navigateur de servir des fichiers individuels.
Le téléchargement de dossiers aurait également d'autres difficultés, comme décrit par Lars Gunther :
Cette proposition […] doit avoir deux contrôles (si elle est réalisable):
Taille maximale, pour empêcher quelqu'un de télécharger un répertoire complet de plusieurs centaines d'images brutes non compressées ...
Filtrage même si l'attribut accept est omis. Les métadonnées Mac OS et les miniatures Windows, etc. doivent être omises. Tous les fichiers et répertoires cachés doivent par défaut être exclus.
Vous pouvez maintenant télécharger des répertoires par glisser-déposer et saisie.
<input type='file' webkitdirectory >
et pour le glisser-déposer (pour les navigateurs webkit).
Gestion des dossiers glisser-déposer.
<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>
Ressources:
http://updates.html5rocks.com/2012/07/Drag-and-drop-a-folder-onto-Chrome-now-available
Firefox prend désormais en charge le téléchargement de dossiers, à partir du 15 novembre 2016, dans la version 50.0: https://developer.mozilla.org/en-US/Firefox/Releases/50#Files_and_directories
Vous pouvez faire glisser et déposer des dossiers dans Firefox ou vous pouvez parcourir et sélectionner un dossier local à télécharger. Il prend également en charge les dossiers imbriqués dans des sous-dossiers.
Cela signifie que vous pouvez désormais utiliser Chrome, Firefox, Edge ou Opera pour télécharger des dossiers. Vous ne pouvez pas utiliser Safari ou Internet Explorer pour le moment.
Cette fonction vous donnera une promesse de tableau de tous les fichiers déposés, comme <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
exemple d'utilisation: https://github.com/grabantot/datatransfer-files-promise/blob/master/index.html
La spécification HTML5 ne dit PAS que lors de la sélection d'un dossier à télécharger, le navigateur doit télécharger tous les fichiers contenus de manière récursive.
En fait, dans Chrome/Chromium, vous pouvez télécharger un dossier, mais lorsque vous le faites, il télécharge simplement un fichier 4KB sans signification, qui représente le répertoire. Certaines applications côté serveur comme Alfresco peuvent détecter cela et avertir l'utilisateur que les dossiers ne peuvent pas être téléchargés:
Voici un exemple complet de la façon d'utiliser API d'entrées de fichier et de répertoire :
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
est pris en charge par Chrome 13+, Firefox 50+ et Edge.
Source: https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/webkitGetAsEntry
HTML5 permet-il le téléchargement par glisser-déposer de dossiers ou d'une arborescence de dossiers?
Seul Chrome prend en charge cette fonctionnalité. Elle n'a pas réussi à tirer parti et est susceptible d'être supprimée.
Réf: https://developer.mozilla.org/en/docs/Web/API/DirectoryReader#readEntries