Comment puis-je compter le nombre de fichiers dans un répertoire en utilisant Java? Pour simplifier, supposons que le répertoire n'a pas de sous-répertoires.
Je connais la méthode standard de:
new File(<directory path>).listFiles().length
Mais cela passera efficacement par tous les fichiers du répertoire, ce qui peut prendre du temps si le nombre de fichiers est important. En outre, je ne me soucie pas des fichiers réels dans le répertoire, sauf si leur nombre est supérieur à un certain grand nombre fixe (disons 5000).
Je suppose, mais le répertoire (ou son i-node dans le cas d'Unix) ne stocke-t-il pas le nombre de fichiers qu'il contient? Si je pouvais obtenir ce nombre directement du système de fichiers, ce serait beaucoup plus rapide. J'ai besoin de faire cette vérification pour chaque requête HTTP sur un serveur Tomcat avant que le back-end ne commence le vrai traitement. Par conséquent, la vitesse est d'une importance primordiale.
Je pouvais exécuter un démon de temps en temps pour effacer le répertoire. Je le sais, alors ne me donnez pas cette solution.
Cela peut ne pas être approprié pour votre application, mais vous pouvez toujours essayer un appel natif (en utilisant jni ou jna ), ou exécuter une commande spécifique à la plate-forme et lire le résultat avant de revenir à list (). longueur. Sur * nix, vous pouvez exécuter ls -1a | wc -l
(note - c'est dash-one-a pour la première commande, et dash-lowercase-L pour la seconde). Je ne sais pas ce qui serait correct sur Windows - peut-être juste un dir
et recherchez le résumé.
Avant de vous embêter avec quelque chose comme ça, je vous recommande fortement de créer un répertoire avec un très grand nombre de fichiers et de voir simplement si list (). Length prend vraiment trop de temps. Comme ce blogueur le suggère, vous ne voudrez peut-être pas transpirer.
J'irais probablement avec la réponse de Varkhan moi-même.
Ah ... la raison pour ne pas avoir une méthode simple dans Java pour ce faire est l'abstraction du stockage de fichiers: certains systèmes de fichiers peuvent ne pas avoir le nombre de fichiers dans un répertoire facilement disponible ... qui comptent peut même ne pas avoir de sens du tout (voir par exemple les systèmes de fichiers P2P distribués, les fs qui stockent les listes de fichiers sous forme de liste liée ou les systèmes de fichiers basés sur une base de données ...).
new File(<directory path>).list().length
est probablement votre meilleur choix.
Depuis Java 8, vous pouvez le faire en trois lignes:
try (Stream<Path> files = Files.list(Paths.get("your/path/here"))) {
long count = files.count();
}
Concernant les 5000 nœuds enfants et aspects inode:
Cette méthode itérera sur les entrées mais comme Varkhan l'a suggéré, vous ne pouvez probablement pas faire mieux que jouer avec JNI ou les appels de commandes système directes, mais même dans ce cas, vous ne pouvez jamais être sûr que ces méthodes ne font pas la même chose!
Cependant, approfondissons un peu ceci:
En regardant la source JDK8, Files.list
expose un stream qui utilise un Iterable
de Files.newDirectoryStream
qui délègue à FileSystemProvider.newDirectoryStream
.
Sur les systèmes UNIX (décompilé Sun.nio.fs.UnixFileSystemProvider.class
), il charge un itérateur: A Sun.nio.fs.UnixSecureDirectoryStream
est utilisé (avec des verrous de fichiers lors de l'itération dans le répertoire).
Il existe donc un itérateur qui parcourra les entrées ici.
Maintenant, regardons le mécanisme de comptage.
Le comptage réel est effectué par l'API de réduction du nombre/de la somme exposée par flux Java 8 . En théorie, cette API peut effectuer des opérations parallèles sans trop d'effort (avec multihtreading). Cependant, le flux est créé avec le parallélisme désactivé, donc c'est un non ...
Le bon côté de cette approche est que il ne chargera pas le tableau en mémoire car les entrées seront comptées par un itérateur au fur et à mesure de leur lecture par l'API sous-jacente (Filesystem).
Enfin, pour les informations, conceptuellement dans un système de fichiers, un nœud de répertoire n'est pas requis pour contenir le nombre des fichiers qu'il contient, il peut juste contenir la liste de ses nœuds enfants (liste des inodes). Je ne suis pas un expert des systèmes de fichiers, mais je crois que les systèmes de fichiers UNIX fonctionnent exactement comme ça. Vous ne pouvez donc pas supposer qu'il existe un moyen d'obtenir directement ces informations (c'est-à-dire qu'il peut toujours y avoir une liste de nœuds enfants cachés quelque part).
Malheureusement, je pense que c'est déjà le meilleur moyen (bien que list()
soit légèrement meilleur que listFiles()
, car il ne construit pas File
objets).
Comme vous n'avez pas vraiment besoin du nombre total et que vous souhaitez en fait effectuer une action après un certain nombre (dans votre cas 5000), vous pouvez utiliser Java.nio.file.Files.newDirectoryStream
. L'avantage est que vous pouvez quitter tôt au lieu d'avoir à parcourir tout le répertoire juste pour obtenir un décompte.
public boolean isOverMax(){
Path dir = Paths.get("C:/foo/bar");
int i = 1;
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path p : stream) {
//larger than max files, exit
if (++i > MAX_FILES) {
return true;
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
return false;
}
interface doc pour DirectoryStream
a également de bons exemples.
Si vous avez des répertoires contenant vraiment (> 100'000) de nombreux fichiers, voici une méthode (non portable):
String directoryPath = "a path";
// -f flag is important, because this way ls does not sort it output,
// which is way faster
String[] params = { "/bin/sh", "-c",
"ls -f " + directoryPath + " | wc -l" };
Process process = Runtime.getRuntime().exec(params);
BufferedReader reader = new BufferedReader(new InputStreamReader(
process.getInputStream()));
String fileCount = reader.readLine().trim() - 2; // accounting for .. and .
reader.close();
System.out.println(fileCount);
Utiliser du cigare devrait aider. Sigar a des crochets natifs pour obtenir les statistiques
new Sigar().getDirStat(dir).getTotal()
Malheureusement, comme l'a dit mmyers, File.list () est à peu près aussi rapide que vous allez utiliser Java. Si la vitesse est aussi importante que vous le dites, vous voudrez peut-être envisager de faire cette opération particulière en utilisant JNI . Vous pouvez ensuite adapter votre code à votre situation particulière et à votre système de fichiers.
public void shouldGetTotalFilesCount() {
Integer reduce = of(listRoots()).parallel().map(this::getFilesCount).reduce(0, ((a, b) -> a + b));
}
private int getFilesCount(File directory) {
File[] files = directory.listFiles();
return Objects.isNull(files) ? 1 : Stream.of(files)
.parallel()
.reduce(0, (Integer acc, File p) -> acc + getFilesCount(p), (a, b) -> a + b);
}