Respostas:
Você pode usar File#isDirectory()para testar se o arquivo (caminho) fornecido é um diretório. Se for esse o caso true, basta chamar o mesmo método novamente com o File#listFiles()resultado. Isso é chamado de recursão .
Aqui está um exemplo básico de kickoff.
public static void main(String... args) {
File[] files = new File("C:/").listFiles();
showFiles(files);
}
public static void showFiles(File[] files) {
for (File file : files) {
if (file.isDirectory()) {
System.out.println("Directory: " + file.getName());
showFiles(file.listFiles()); // Calls same method again.
} else {
System.out.println("File: " + file.getName());
}
}
}
Observe que isso é sensível a StackOverflowErrorquando a árvore é mais profunda do que a pilha da JVM pode suportar. Você pode usar uma abordagem iterativa ou recursão de cauda , mas esse é outro assunto;)
NullPointerExceptionquando o sistema de arquivos muda entre a chamada isDirectorye, listFilescomo pode acontecer, se o System.out.printlnbloqueio for bloqueado ou você simplesmente tiver azar. Verificar se a saída de listFilesnão é nula resolveria essa condição de corrida.
java.nio.file.DirectoryStreampermitirá a iteração em um diretório e poderá ser implementado para ter uma pequena quantidade de memória, mas a única maneira de dizer com certeza seria para monitorar o uso de memória em uma plataforma específica.
Se você estiver usando o Java 1.7, poderá usar java.nio.file.Files.walkFileTree(...).
Por exemplo:
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();
}
}
}
Se você estiver usando o Java 8, poderá usar a interface de fluxo com 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();
}
}
}
Confira a classe FileUtils no Apache Commons - especificamente iterateFiles :
Permite a iteração sobre os arquivos no diretório especificado (e opcionalmente em seus subdiretórios).
Para Java 7+, também há https://docs.oracle.com/javase/7/docs/api/java/nio/file/DirectoryStream.html
Exemplo retirado do 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;
}
É uma árvore, então a recursão é sua amiga: comece com o diretório pai e chame o método para obter uma matriz de arquivos filhos. Iterar através da matriz filho. Se o valor atual for um diretório, passe-o para uma chamada recursiva do seu método. Caso contrário, processe o arquivo folha adequadamente.
Como observado, esse é um problema de recursão. Em particular, você pode querer olhar para
listFiles()
Na API do arquivo java aqui . Retorna uma matriz de todos os arquivos em um diretório. Usando isso junto com
isDirectory()
ver se é necessário recuar mais é um bom começo.
Para adicionar com a resposta @msandiford, como na maioria das vezes em que uma árvore de arquivos é percorrida, você pode querer executar uma função como um diretório ou visitar qualquer arquivo específico. Se você estiver relutante em usar fluxos. Os seguintes métodos substituídos podem ser implementados
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;
}
});
Você também pode usar incorretamente o File.list (FilenameFilter) (e variantes) para a passagem do arquivo. Código curto e funciona em versões java anteriores, por exemplo:
// 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;
}
});