web-dev-qa-db-fra.com

Liste tous les fichiers d’un répertoire de manière récursive avec Java

J'ai cette fonction qui imprime le nom de tous les fichiers d'un répertoire de manière récursive. Le problème est que mon code est très lent car il doit accéder à un périphérique réseau distant à chaque itération.

Mon plan est de commencer par charger tous les fichiers du répertoire de manière récursive, puis de parcourir tous les fichiers avec la regex pour filtrer tous les fichiers que je ne souhaite pas. Quelqu'un a-t-il une meilleure suggestion?

public static printFnames(String sDir){
  File[] faFiles = new File(sDir).listFiles();
  for(File file: faFiles){
    if(file.getName().matches("^(.*?)")){
      System.out.println(file.getAbsolutePath());
    }
    if(file.isDirectory()){
      printFnames(file.getAbsolutePath());
    }
  }
}

Ceci est juste un test plus tard, je ne vais pas utiliser le code comme ceci, mais plutôt ajouter le chemin et la date de modification de chaque fichier qui correspond à une expression rationnelle avancée à un tableau.

80
Hultner

En supposant qu'il s'agisse du code de production réel que vous allez écrire, je suggère d'utiliser la solution à ce type de problème déjà résolu - Apache Commons IO , plus précisément FileUtils.listFiles() . Il gère les répertoires imbriqués, les filtres (basés sur le nom, l'heure de modification, etc.).

Par exemple, pour votre regex:

Collection files = FileUtils.listFiles(
  dir, 
  new RegexFileFilter("^(.*?)"), 
  DirectoryFileFilter.DIRECTORY
);

Cela recherchera de manière récursive les fichiers correspondant à la regex ^(.*?), en renvoyant les résultats sous forme de collection.

Cela vaut la peine de noter que ce ne sera pas plus rapide que de faire rouler votre propre code, mais qu’il fait la même chose: balayer un système de fichiers dans Java est tout simplement lent. La différence est que la version d’Apache Commons n’a pas bugs en elle.

129
skaffman

Dans Java 8, il s’agit d’un 1-liner via Files.find() > avec une profondeur arbitrairement grande (par exemple 999) Et - BasicFileAttributes de isRegularFile()

public static printFnames(String sDir) {
    Files.find(Paths.get(sDir), 999, (p, bfa) -> bfa.isRegularFile()).forEach(System.out::println);
}

Pour ajouter davantage de filtrage, améliorez le lambda, par exemple tous les fichiers jpg modifiés au cours des dernières 24 heures:

(p, bfa) -> bfa.isRegularFile()
  && p.getFileName().toString().matches(".*\\.jpg")
  && bfa.lastModifiedTime().toMillis() > System.currentMillis() - 86400000
58
Bohemian

C'est une méthode récursive très simple pour obtenir tous les fichiers à partir d'une racine donnée.

Il utilise la classe Java 7 NIO Path).

private List<String> getFileNames(List<String> fileNames, Path dir) {
    try(DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
        for (Path path : stream) {
            if(path.toFile().isDirectory()) {
                getFileNames(fileNames, path);
            } else {
                fileNames.add(path.toAbsolutePath().toString());
                System.out.println(path.getFileName());
            }
        }
    } catch(IOException e) {
        e.printStackTrace();
    }
    return fileNames;
} 
25
Dan

Avec Java 7, un moyen plus rapide de parcourir une arborescence de répertoires a été introduit avec les fonctionnalités Paths et Files. Elles sont beaucoup plus rapides que l’ancien. File manière.

Ce serait le code pour parcourir et vérifier les noms de chemins avec une expression régulière:

public final void test() throws IOException, InterruptedException {
    final Path rootDir = Paths.get("path to your directory where the walk starts");

    // Walk thru mainDir directory
    Files.walkFileTree(rootDir, new FileVisitor<Path>() {
        // First (minor) speed up. Compile regular expression pattern only one time.
        private Pattern pattern = Pattern.compile("^(.*?)");

        @Override
        public FileVisitResult preVisitDirectory(Path path,
                BasicFileAttributes atts) throws IOException {

            boolean matches = pattern.matcher(path.toString()).matches();

            // TODO: Put here your business logic when matches equals true/false

            return (matches)? FileVisitResult.CONTINUE:FileVisitResult.SKIP_SUBTREE;
        }

        @Override
        public FileVisitResult visitFile(Path path, BasicFileAttributes mainAtts)
                throws IOException {

            boolean matches = pattern.matcher(path.toString()).matches();

            // TODO: Put here your business logic when matches equals true/false

            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult postVisitDirectory(Path path,
                IOException exc) throws IOException {
            // TODO Auto-generated method stub
            return FileVisitResult.CONTINUE;
        }

        @Override
        public FileVisitResult visitFileFailed(Path path, IOException exc)
                throws IOException {
            exc.printStackTrace();

            // If the root directory has failed it makes no sense to continue
            return path.equals(rootDir)? FileVisitResult.TERMINATE:FileVisitResult.CONTINUE;
        }
    });
}
18
jboi

Le moyen rapide d’obtenir le contenu d’un répertoire à l’aide de Java 7 NIO:

import Java.nio.file.DirectoryStream;
import Java.nio.file.Files;
import Java.nio.file.FileSystems;
import Java.nio.file.Path;

...

Path dir = FileSystems.getDefault().getPath( filePath );
DirectoryStream<Path> stream = Files.newDirectoryStream( dir );
for (Path path : stream) {
   System.out.println( path.getFileName() );
}
stream.close();
13
RealHowTo

L'interface de Java pour la lecture du contenu du dossier du système de fichiers n'est pas très performante (comme vous l'avez découvert). JDK 7 corrige cela avec une toute nouvelle interface pour ce genre de choses, ce qui devrait apporter des performances de niveau natif à ce type d'opérations.

Le problème principal est que Java effectue un appel système natif pour chaque fichier. Sur une interface à faible temps de latence, ce n’est pas un gros problème - mais sur un réseau avec une latence même modérée, Si vous profilez votre algorithme ci-dessus, vous constaterez que la plus grande partie du temps est consacrée à l'appel fastidieux isDirectory (), car vous effectuez un aller-retour pour chaque appel unique à isDirectory (). Les systèmes d'exploitation peuvent fournir ce type d'informations lorsque la liste de fichiers/dossiers a été demandée à l'origine (par opposition à la recherche de propriétés sur chaque chemin de fichier individuel).

Si vous ne pouvez pas attendre pour JDK7, une stratégie pour traiter cette latence consiste à utiliser plusieurs threads et à utiliser un service d'exécution (ExecutorService) avec un nombre maximal de threads pour effectuer votre récursivité. Ce n'est pas génial (vous devez faire face au verrouillage de vos structures de données en sortie), mais ce sera beaucoup plus rapide que de faire cela en un seul thread.

Dans toutes vos discussions sur ce genre de choses, je vous recommande vivement de comparer le meilleur de ce que vous pourriez faire en utilisant du code natif (ou même un script en ligne de commande qui fait à peu près la même chose). Dire qu'il faut une heure pour parcourir une structure de réseau ne signifie pas grand-chose. En nous disant que vous pouvez le faire en natif en 7 secondes, mais cela prend une heure en Java va attirer l'attention des gens.

12
Kevin Day

cela fonctionnera très bien ... et sa récursive

File root = new File("ROOT PATH");
for ( File file : root.listFiles())
{
    getFilesRecursive(file);
}


private static void getFilesRecursive(File pFile)
{
    for(File files : pFile.listFiles())
    {
        if(files.isDirectory())
        {
            getFilesRecursive(files);
        }
        else
        {
            // do your thing 
            // you can either save in HashMap and use it as
            // per your requirement
        }
    }
}
5
Prathamesh sawant

Personnellement, j'aime bien cette version de FileUtils. Voici un exemple qui trouve tous les mp3 ou flacs dans un répertoire ou l’un de ses sous-répertoires:

String[] types = {"mp3", "flac"};
Collection<File> files2 = FileUtils.listFiles(/path/to/your/dir, types , true);
3
thouliha

Cela fonctionnera bien

public void displayAll(File path){      
    if(path.isFile()){
        System.out.println(path.getName());
    }else{
        System.out.println(path.getName());         
        File files[] = path.listFiles();
        for(File dirOrFile: files){
            displayAll(dirOrFile);
        }
    }
}
3
Mam's

Cette fonction listera probablement tout le nom du fichier et son chemin depuis son répertoire et ses sous-répertoires.

public void listFile(String pathname) {
    File f = new File(pathname);
    File[] listfiles = f.listFiles();
    for (int i = 0; i < listfiles.length; i++) {
        if (listfiles[i].isDirectory()) {
            File[] internalFile = listfiles[i].listFiles();
            for (int j = 0; j < internalFile.length; j++) {
                System.out.println(internalFile[j]);
                if (internalFile[j].isDirectory()) {
                    String name = internalFile[j].getAbsolutePath();
                    listFile(name);
                }

            }
        } else {
            System.out.println(listfiles[i]);
        }

    }

}
1
Vishal Mokal

Le moyen le plus efficace que j'ai trouvé pour gérer des millions de dossiers et de fichiers consiste à capturer la liste des répertoires via une commande DOS dans un fichier et à l'analyser. Une fois que vous avez analysé les données, vous pouvez analyser et calculer des statistiques.

0
Kiran

Java 8

public static void main(String[] args) throws IOException {

        Path start = Paths.get("C:\\data\\");
        try (Stream<Path> stream = Files.walk(start, Integer.MAX_VALUE)) {
            List<String> collect = stream
                .map(String::valueOf)
                .sorted()
                .collect(Collectors.toList());

            collect.forEach(System.out::println);
        }


    }
0
Niraj Sonawane

on a l'impression que c'est stupide d'accéder au système de fichiers et d'obtenir le contenu de chaque sous-répertoire au lieu de tout récupérer en même temps.

Votre sentiment est faux. C'est comme ça que fonctionnent les systèmes de fichiers. Il n’existe pas de moyen plus rapide (sauf lorsque vous devez le faire de manière répétée ou pour différents modèles, vous pouvez mettre en mémoire cache tous les chemins de fichiers, mais vous devez ensuite traiter l’invalidation de la mémoire cache, c’est-à-dire ce qui se passe lorsque des fichiers sont ajoutés/supprimés/renommés l'application s'exécute).

0
Michael Borgwardt

Un autre code optimisé

import Java.io.File;
import Java.util.ArrayList;
import Java.util.List;

public class GetFilesRecursive {
    public static List <String> getFilesRecursively(File dir){
        List <String> ls = new ArrayList<String>();
        if (dir.isDirectory())
            for (File fObj : dir.listFiles()) {
                if(fObj.isDirectory()) {
                    ls.add(String.valueOf(fObj));
                    ls.addAll(getFilesRecursively(fObj));               
                } else {
                    ls.add(String.valueOf(fObj));       
                }
            }
        else
            ls.add(String.valueOf(dir));

        return ls;
    }

    public static void main(String[] args) {
        List <String> ls = getFilesRecursively(new File("/Users/srinivasab/Documents"));
        for (String file:ls) {
            System.out.println(file);
        }
        System.out.println(ls.size());
    }
}
0
Sri
import Java.io.*;

public class MultiFolderReading {

public void checkNoOfFiles (String filename) throws IOException {

    File dir=new File(filename);
    File files[]=dir.listFiles();//files array stores the list of files

 for(int i=0;i<files.length;i++)
    {
        if(files[i].isFile()) //check whether files[i] is file or directory
        {
            System.out.println("File::"+files[i].getName());
            System.out.println();

        }
        else if(files[i].isDirectory())
        {
            System.out.println("Directory::"+files[i].getName());
            System.out.println();
            checkNoOfFiles(files[i].getAbsolutePath());
        }
    }
}

public static void main(String[] args) throws IOException {

    MultiFolderReading mf=new MultiFolderReading();
    String str="E:\\file"; 
    mf.checkNoOfFiles(str);
   }
}
0
prajakta
public class GetFilesRecursive {
    public static List <String> getFilesRecursively(File dir){
        List <String> ls = new ArrayList<String>();
        for (File fObj : dir.listFiles()) {
            if(fObj.isDirectory()) {
                ls.add(String.valueOf(fObj));
                ls.addAll(getFilesRecursively(fObj));               
            } else {
                ls.add(String.valueOf(fObj));       
            }
        }

        return ls;
    }
    public static List <String> getListOfFiles(String fullPathDir) {
        List <String> ls = new ArrayList<String> ();
        File f = new File(fullPathDir);
        if (f.exists()) {
            if(f.isDirectory()) {
                ls.add(String.valueOf(f));
                ls.addAll(getFilesRecursively(f));
            }
        } else {
            ls.add(fullPathDir);
        }
        return ls;
    }

    public static void main(String[] args) {
        List <String> ls = getListOfFiles("/Users/srinivasab/Documents");
        for (String file:ls) {
            System.out.println(file);
        }
        System.out.println(ls.size());
    }
}
0
Sri

Juste pour que vous sachiez que isDirectory () est une méthode assez lente. Je trouve cela assez lent dans mon navigateur de fichiers. Je vais chercher dans une bibliothèque pour la remplacer par du code natif.

0
Daniel Ryan

Dans Guava, vous n'avez pas à attendre qu'une collection vous soit renvoyée, mais vous pouvez en fait parcourir les fichiers. Il est facile d’imaginer une interface IDoSomethingWithThisFile dans la signature de la fonction ci-dessous:

public static void collectFilesInDir(File dir) {
    TreeTraverser<File> traverser = Files.fileTreeTraverser();
    FluentIterable<File> filesInPostOrder = traverser.preOrderTraversal(dir);
    for (File f: filesInPostOrder)
        System.out.printf("File: %s\n", f.getPath());
}

TreeTraverser vous permet également de choisir entre différents styles de traversée.

0