How do I iterate through the files in a directory and it's sub-directories in Java?
Asked Answered
B

10

204

I need to get a list of all the files in a directory, including files in all the sub-directories. What is the standard way to accomplish directory iteration with Java?

Bartholemy answered 1/7, 2010 at 1:2 Comment(0)
P
247

You can use File#isDirectory() to test if the given file (path) is a directory. If this is true, then you just call the same method again with its File#listFiles() outcome. This is called recursion.

Here's a basic kickoff example:

package com.stackoverflow.q3154488;

import java.io.File;

public class Demo {

    public static void main(String... args) {
        File dir = new File("/path/to/dir");
        showFiles(dir.listFiles());
    }

    public static void showFiles(File[] files) {
        for (File file : files) {
            if (file.isDirectory()) {
                System.out.println("Directory: " + file.getAbsolutePath());
                showFiles(file.listFiles()); // Calls same method again.
            } else {
                System.out.println("File: " + file.getAbsolutePath());
            }
        }
    }
}

Note that this is sensitive to StackOverflowError when the tree is deeper than the JVM's stack can hold. If you're already on Java 8 or newer, then you'd better use Files#walk() instead which utilizes tail recursion:

package com.stackoverflow.q3154488;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class DemoWithJava8 {

    public static void main(String... args) throws Exception {
        Path dir = Paths.get("/path/to/dir");
        Files.walk(dir).forEach(path -> showFile(path.toFile()));
    }

    public static void showFile(File file) {
        if (file.isDirectory()) {
            System.out.println("Directory: " + file.getAbsolutePath());
        } else {
            System.out.println("File: " + file.getAbsolutePath());
        }
    }
}
Pironi answered 1/7, 2010 at 1:13 Comment(6)
thanks Balus, any idea on how deep that may be as a general guess?Bartholemy
Depends on your JVM's memory settings. But generally something like a few thousand. If you think you might ever run into a directory like that, then don't use recursion.Overripe
This is susceptible to a NullPointerException when the file system changes between the call to isDirectory and listFiles as might happen if System.out.println blocks or you just get really unlucky. Checking that the output of listFiles is not null would solve that race condition.Kei
@MikeSamuel - If the directory has 10's of thousands of small files with big names, and you store all file names in an array at once, then JVM could run out of memory, right ? So, is there a way to iterate one by one or in small batches ?Voiceful
@BoratSagdiyev, Not using the old Java file APIs, but if you're on a modern JVM then the java.nio.file.DirectoryStream allows you to iterate over a directory, and could be implemented to have a small memory footprint but the only way to tell for sure would be to monitor memory usage on a particular platform.Kei
Regarding the comment for usage of DirectoryStream please refer to the answer of Wim Deblauwe as it lists important aspect. The stream should be closed to prevent resource leak.Drawee
H
100

If you are using Java 1.7, you can use java.nio.file.Files.walkFileTree(...).

For example:

public class WalkFileTreeExample {

  public static void main(String[] args) {
    Path p = Paths.get("/usr");
    FileVisitor<Path> fv = new SimpleFileVisitor<Path>() {
      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
          throws IOException {
        System.out.println(file);
        return FileVisitResult.CONTINUE;
      }
    };

    try {
      Files.walkFileTree(p, fv);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}

If you are using Java 8, you can use the stream interface with java.nio.file.Files.walk(...):

public class WalkFileTreeExample {

  public static void main(String[] args) {
    try (Stream<Path> paths = Files.walk(Paths.get("/usr"))) {
      paths.forEach(System.out::println);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

}
Hockenberry answered 1/7, 2010 at 1:55 Comment(2)
is there a way with the streams to put a checkpoint when a new directory is walked and execute a function?Towandatoward
Files.walk(somePath) will walk you through the whole file and directory tree accessible from that root directory. If you need to process only certain files, i.e. by their extension, you can use a filter with PathMatcher that you configured to glob:**.xml i.e. to omit any non-XML files in later stages of the pipelineBarnwell
A
29

Check out the FileUtils class in Apache Commons - specifically iterateFiles:

Allows iteration over the files in given directory (and optionally its subdirectories).

Audile answered 1/7, 2010 at 1:7 Comment(3)
This API isn't truly streaming (if you care about mem usage), it first generate a collection, only then returns an iterator over it: return listFiles(directory, fileFilter, dirFilter).iterator();Ashford
Good option for Java 1.6.Estuarine
Agree with @GiliNachum. FileUtils by Apache first collects all files and gives an iterator for them. It is harmful for resources if you have a huge amount of files.Gutsy
S
9

Using org.apache.commons.io.FileUtils

File file = new File("F:/Lines");       
Collection<File> files = FileUtils.listFiles(file, null, true);     
for(File file2 : files){
    System.out.println(file2.getName());            
} 

Use false if you do not want files from sub directories.

Statism answered 7/11, 2014 at 17:59 Comment(0)
A
8

For Java 7+, there is also https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html

Example taken from the Javadoc:

List<Path> listSourceFiles(Path dir) throws IOException {
   List<Path> result = new ArrayList<>();
   try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.{c,h,cpp,hpp,java}")) {
       for (Path entry: stream) {
           result.add(entry);
       }
   } catch (DirectoryIteratorException ex) {
       // I/O error encounted during the iteration, the cause is an IOException
       throw ex.getCause();
   }
   return result;
}
Abloom answered 16/12, 2015 at 13:9 Comment(0)
F
3

It's a tree, so recursion is your friend: start with the parent directory and call the method to get an array of child Files. Iterate through the child array. If the current value is a directory, pass it to a recursive call of your method. If not, process the leaf file appropriately.

Fixed answered 1/7, 2010 at 1:6 Comment(0)
G
2

As noted, this is a recursion problem. In particular, you may want to look at

listFiles() 

In the java File API here. It returns an array of all the files in a directory. Using this along with

isDirectory()

to see if you need to recurse further is a good start.

Gratification answered 1/7, 2010 at 1:12 Comment(1)
This link may be of use since the one in the answer is broken.Nisse
P
1

You can also misuse File.list(FilenameFilter) (and variants) for file traversal. Short code and works in early java versions, e.g:

// list files in dir
new File(dir).list(new FilenameFilter() {
    public boolean accept(File dir, String name) {
        String file = dir.getAbsolutePath() + File.separator + name;
        System.out.println(file);
        return false;
    }
});
Pastypat answered 29/6, 2019 at 17:20 Comment(0)
T
0

To add with @msandiford answer, as most of the times when a file tree is walked u may want to execute a function as a directory or any particular file is visited. If u are reluctant to using streams. The following methods overridden can be implemented

Files.walkFileTree(Paths.get(Krawl.INDEXPATH), EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
    new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
                throws IOException {
                // Do someting before directory visit
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                throws IOException {
                // Do something when a file is visited
                return FileVisitResult.CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc)
                throws IOException {
                // Do Something after directory visit 
                return FileVisitResult.CONTINUE;
        }
});
Towandatoward answered 27/12, 2017 at 3:45 Comment(0)
D
0

I like to use Optional and streams to have a net and clear solution, i use the below code to iterate over a directory. the below cases are handled by the code:

  1. handle the case of empty directory
  2. Laziness

but as mentioned by others, you still have to pay attention for outOfMemory in case you have huge folders

    File directoryFile = new File("put your path here");
    Stream<File> files = Optional.ofNullable(directoryFile// directoryFile
                                                          .listFiles(File::isDirectory)) // filter only directories(change with null if you don't need to filter)
                                 .stream()
                                 .flatMap(Arrays::stream);// flatmap from Stream<File[]> to Stream<File>
Downtown answered 10/11, 2020 at 14:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.