web-dev-qa-db-fra.com

Compter le nombre de fichiers dans un répertoire en utilisant Java

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.

60
euphoria83

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.

12
Marty Lamb

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.

79
Varkhan

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).

27
superbob

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).

16
Michael Myers

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.

6
mateuscb

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);
4
Renaud

Utiliser du cigare devrait aider. Sigar a des crochets natifs pour obtenir les statistiques

new Sigar().getDirStat(dir).getTotal()
2
user2162827

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.

1
Sebastian Celis
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);
}
1
Sergii Povzaniuk