Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java Files.walk: close the stream that owns directory handles

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

Files.walk returns a lazy stream over a directory tree and may hold open directory resources until the stream closes.

The terminal operation is not cleanup

The program creates two temporary receipt files, walks one level, sorts their names and prints them. The stream sits inside try-with-resources; it closes even if traversal throws. A terminal collect alone does not state that directory handles were released.

The temporary fixture removes its files afterward. A production crawler needs policies for symlinks, permission errors, maximum depth and files that change during traversal. Stream ownership and close failures cover related rules.

Do not trust path text as a containment check

A walk over user-selected roots needs explicit authorization and symlink handling. Normalizing a path string is not sufficient protection against races or links. Archive path validation addresses another untrusted-path boundary.

Working program

Java
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ReceiptDirectoryScan {
    public static void main(String[] args) throws Exception {
        Path root = Files.createTempDirectory("aitrove-receipts-");
        Path first = root.resolve("a.txt");
        Path second = root.resolve("b.txt");
        try {
            Files.write(first, new byte[0]);
            Files.write(second, new byte[0]);
            try (Stream<Path> found = Files.walk(root, 1)) {
                List<String> names = found.filter(path -> Files.isRegularFile(path))
                    .map(path -> path.getFileName().toString()).sorted().collect(Collectors.toList());
                System.out.println(names);
            }
        } finally {
            Files.deleteIfExists(first);
            Files.deleteIfExists(second);
            Files.deleteIfExists(root);
        }
    }
}

Output

Output
[a.txt, b.txt]

Costs and boundaries

The walk must visit the selected entries and sorting k names takes O(k log k) comparisons plus O(k) name storage. The depth-one fixture is not a benchmark for a large or changing filesystem.

Common Mistakes

  • Do not leave a Files.walk stream open after collecting its elements.
  • Do not assume a live directory tree is a frozen snapshot.
  • Do not treat a normalized path as protection from symlink races.

Read next

Java file I/O: UTF-8, streaming reads, and path ownership, Java Stream.close: terminal traversal does not close every source, Java try-with-resources: close order and suppressed failures, Java ZIP inputs: bounded staging and rejected path traversal.

java
files-walk-close
Storage details