Como listar recursivamente todos os arquivos em um diretório em Java? A estrutura fornece algum utilitário?
Eu vi muitas implementações hacky. Mas nenhum da estrutura ou nio
Como listar recursivamente todos os arquivos em um diretório em Java? A estrutura fornece algum utilitário?
Eu vi muitas implementações hacky. Mas nenhum da estrutura ou nio
Respostas:
O Java 8 fornece um bom fluxo para processar todos os arquivos em uma árvore.
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
Isso fornece uma maneira natural de percorrer arquivos. Como é um fluxo, você pode realizar todas as operações de fluxo agradáveis no resultado, como limite, agrupamento, mapeamento, saída antecipada etc.
UPDATE : Eu posso apontar que também existe o Files.find, que utiliza um BiPredicate que pode ser mais eficiente se você precisar verificar os atributos do arquivo.
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
Observe que, embora o JavaDoc ilude que esse método possa ser mais eficiente que o Files.walk, ele é efetivamente idêntico, a diferença de desempenho pode ser observada se você também estiver recuperando atributos de arquivo em seu filtro. No final, se você precisar filtrar os atributos, use Files.find , caso contrário, use Files.walk , principalmente porque há sobrecargas e é mais conveniente.
TESTES : Conforme solicitado, forneço uma comparação de desempenho de muitas das respostas. Confira o projeto Github, que contém resultados e um caso de teste .
Files.walk
com um fluxo paralelo é o melhor, seguido de perto pelo Files.walkFileTree
qual é apenas um pouco mais lento. A resposta aceita usando commons-io é de longe a mais lenta em meus testes, sendo 4 vezes mais lenta.
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
. Como eu poderia corrigi-lo
Os FileUtils têm iterateFiles
e listFiles
métodos. Faça uma tentativa. (de commons-io )
Edit: Você pode conferir aqui uma referência de diferentes abordagens. Parece que a abordagem commons-io é lenta, então escolha algumas das mais rápidas daqui (se for o caso)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
, onde dir
é um objeto File que aponta para o diretório base.
listFilesAndDirs()
, pois listFiles()
não retorna pastas vazias.
FileUtils.listFiles(dir, true, true)
. using FileUtils.listFiles(dir, null, true)
lançará uma exceção, enquanto FileUtils.listFiles(dir, true, null)
listará todos os arquivos sem procurar em subdiretórios.
// Pronto para correr
import java.io.File;
public class Filewalker {
public void walk( String path ) {
File root = new File( path );
File[] list = root.listFiles();
if (list == null) return;
for ( File f : list ) {
if ( f.isDirectory() ) {
walk( f.getAbsolutePath() );
System.out.println( "Dir:" + f.getAbsoluteFile() );
}
else {
System.out.println( "File:" + f.getAbsoluteFile() );
}
}
}
public static void main(String[] args) {
Filewalker fw = new Filewalker();
fw.walk("c:\\" );
}
}
-> .
.
"/"
, "./"
ou "../"
para diretório raiz, diretório de trabalho atual e diretório pai, respectivamente
Java 7 terá tem Files.walkFileTree :
Se você fornecer um ponto de partida e um visitante do arquivo, ele invocará vários métodos no visitante do arquivo enquanto ele percorre o arquivo na árvore de arquivos. Esperamos que as pessoas usem isso se estiverem desenvolvendo uma cópia recursiva, uma movimentação recursiva, uma exclusão recursiva ou uma operação recursiva que defina permissões ou execute outra operação em cada um dos arquivos.
Agora existe um tutorial completo do Oracle sobre esta questão .
Nenhuma biblioteca externa é necessária.
Retorna uma coleção para que você possa fazer o que quiser com ela após a chamada.
public static Collection<File> listFileTree(File dir) {
Set<File> fileTree = new HashSet<File>();
if(dir==null||dir.listFiles()==null){
return fileTree;
}
for (File entry : dir.listFiles()) {
if (entry.isFile()) fileTree.add(entry);
else fileTree.addAll(listFileTree(entry));
}
return fileTree;
}
Eu iria com algo como:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
O System.out.println está lá apenas para indicar para fazer algo com o arquivo. não há necessidade de diferenciar arquivos e diretórios, pois um arquivo normal terá simplesmente zero filhos.
listFiles()
: “Se esse nome de caminho abstrato não indicar um diretório, esse método retornará null
.”
Prefiro usar uma fila em vez de recursão para esse tipo de deslocamento simples:
List<File> allFiles = new ArrayList<File>();
Queue<File> dirs = new LinkedList<File>();
dirs.add(new File("/start/dir/"));
while (!dirs.isEmpty()) {
for (File f : dirs.poll().listFiles()) {
if (f.isDirectory()) {
dirs.add(f);
} else if (f.isFile()) {
allFiles.add(f);
}
}
}
basta escrever você mesmo usando recursão simples:
public List<File> addFiles(List<File> files, File dir)
{
if (files == null)
files = new LinkedList<File>();
if (!dir.isDirectory())
{
files.add(dir);
return files;
}
for (File file : dir.listFiles())
addFiles(files, file);
return files;
}
Eu acho que isso deve fazer o trabalho:
File dir = new File(dirname);
String[] files = dir.list();
Dessa forma, você tem arquivos e diretórios. Agora use recursão e faça o mesmo para dirs (a File
classe possui o isDirectory()
método).
Com o Java 7, você pode usar a seguinte classe:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class MyFileIterator extends SimpleFileVisitor<Path>
{
public MyFileIterator(String path) throws Exception
{
Files.walkFileTree(Paths.get(path), this);
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attributes) throws IOException
{
System.out.println("File: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attributes) throws IOException
{
System.out.println("Dir: " + dir);
return FileVisitResult.CONTINUE;
}
}
No Java 8, agora podemos usar o utilitário Arquivos para percorrer uma árvore de arquivos. Muito simples.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
Este código está pronto para ser executado
public static void main(String... args) {
File[] files = new File("D:/").listFiles();
if (files != null)
getFiles(files);
}
public static void getFiles(File[] files) {
for (File file : files) {
if (file.isDirectory()) {
getFiles(file.listFiles());
} else {
System.out.println("File: " + file);
}
}
}
Além da travessia recursiva, também é possível usar uma abordagem baseada no visitante.
O código abaixo usa a abordagem baseada no visitante para a travessia. Espera-se que a entrada do programa seja o diretório raiz a ser percorrido.
public interface Visitor {
void visit(DirElement d);
void visit(FileElement f);
}
public abstract class Element {
protected File rootPath;
abstract void accept(Visitor v);
@Override
public String toString() {
return rootPath.getAbsolutePath();
}
}
public class FileElement extends Element {
FileElement(final String path) {
rootPath = new File(path);
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
}
public class DirElement extends Element implements Iterable<Element> {
private final List<Element> elemList;
DirElement(final String path) {
elemList = new ArrayList<Element>();
rootPath = new File(path);
for (File f : rootPath.listFiles()) {
if (f.isDirectory()) {
elemList.add(new DirElement(f.getAbsolutePath()));
} else if (f.isFile()) {
elemList.add(new FileElement(f.getAbsolutePath()));
}
}
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
public Iterator<Element> iterator() {
return elemList.iterator();
}
}
public class ElementWalker {
private final String rootDir;
ElementWalker(final String dir) {
rootDir = dir;
}
private void traverse() {
Element d = new DirElement(rootDir);
d.accept(new Walker());
}
public static void main(final String[] args) {
ElementWalker t = new ElementWalker("C:\\temp");
t.traverse();
}
private class Walker implements Visitor {
public void visit(final DirElement d) {
System.out.println(d);
for(Element e:d) {
e.accept(this);
}
}
public void visit(final FileElement f) {
System.out.println(f);
}
}
}
Você pode usar o código abaixo para obter recursivamente uma lista de arquivos de pastas ou diretórios específicos.
public static void main(String args[]) {
recusiveList("D:");
}
public static void recursiveList(String path) {
File f = new File(path);
File[] fl = f.listFiles();
for (int i = 0; i < fl.length; i++) {
if (fl[i].isDirectory() && !fl[i].isHidden()) {
System.out.println(fl[i].getAbsolutePath());
recusiveList(fl[i].getAbsolutePath());
} else {
System.out.println(fl[i].getName());
}
}
}
A resposta aceita é ótima, porém é interrompida quando você deseja fazer IO dentro do lambda.
Aqui está o que você pode fazer se sua ação declarar IOExceptions.
Você pode tratar o fluxo filtrado como um Iterable
e executar sua ação regularmente para cada loop. Dessa forma, você não precisa lidar com exceções dentro de uma lambda.
try (Stream<Path> pathStream = Files.walk(Paths.get(path))
.filter(Files::isRegularFile)) {
for (Path file : (Iterable<Path>) pathStream::iterator) {
// something that throws IOException
Files.copy(file, System.out);
}
}
Encontrei esse truque aqui: https://stackoverflow.com/a/32668807/1207791
BFS não recursivo com uma única lista (exemplo particular está pesquisando arquivos * .eml):
final FileFilter filter = new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith(".eml");
}
};
// BFS recursive search
List<File> queue = new LinkedList<File>();
queue.addAll(Arrays.asList(dir.listFiles(filter)));
for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
File file = itr.next();
if (file.isDirectory()) {
itr.remove();
for (File f: file.listFiles(filter)) itr.add(f);
}
}
Minha versão (é claro que eu poderia ter usado a caminhada incorporada no Java 8 ;-)):
public static List<File> findFilesIn(File rootDir, Predicate<File> predicate) {
ArrayList<File> collected = new ArrayList<>();
walk(rootDir, predicate, collected);
return collected;
}
private static void walk(File dir, Predicate<File> filterFunction, List<File> collected) {
Stream.of(listOnlyWhenDirectory(dir))
.forEach(file -> walk(file, filterFunction, addAndReturn(collected, file, filterFunction)));
}
private static File[] listOnlyWhenDirectory(File dir) {
return dir.isDirectory() ? dir.listFiles() : new File[]{};
}
private static List<File> addAndReturn(List<File> files, File toAdd, Predicate<File> filterFunction) {
if (filterFunction.test(toAdd)) {
files.add(toAdd);
}
return files;
}
Aqui está uma solução simples, mas perfeitamente funcional, usando recursion
:
public static List<Path> listFiles(String rootDirectory)
{
List<Path> files = new ArrayList<>();
listFiles(rootDirectory, files);
return files;
}
private static void listFiles(String path, List<Path> collectedFiles)
{
File root = new File(path);
File[] files = root.listFiles();
if (files == null)
{
return;
}
for (File file : files)
{
if (file.isDirectory())
{
listFiles(file.getAbsolutePath(), collectedFiles);
} else
{
collectedFiles.add(file.toPath());
}
}
}
private void fillFilesRecursively(File file, List<File> resultFiles) {
if (file.isFile()) {
resultFiles.add(file);
} else {
for (File child : file.listFiles()) {
fillFilesRecursively(child, resultFiles);
}
}
}
Eu vim com isso para imprimir todos os arquivos / nomes de arquivos recursivamente.
private static void printAllFiles(String filePath,File folder) {
if(filePath==null) {
return;
}
File[] files = folder.listFiles();
for(File element : files) {
if(element.isDirectory()) {
printAllFiles(filePath,element);
} else {
System.out.println(" FileName "+ element.getName());
}
}
}
Exemplo gera arquivos * .csv em subdiretórios de pesquisa recursiva de diretório usando Files.find () de java.nio:
String path = "C:/Daten/ibiss/ferret/";
logger.debug("Path:" + path);
try (Stream<Path> fileList = Files.find(Paths.get(path), Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile() && filePath.toString().endsWith("csv"))) {
List<String> someThingNew = fileList.sorted().map(String::valueOf).collect(Collectors.toList());
for (String t : someThingNew) {
t.toString();
logger.debug("Filename:" + t);
}
}
Postando este exemplo, como tive problemas para entender como passar o parâmetro filename no exemplo # 1 dado por Bryan, usando foreach em Stream-result -
Espero que isto ajude.
Kotlin tem FileTreeWalk
para esse fim. Por exemplo:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
Produzirá uma lista de texto de todos os arquivos não pertencentes ao diretório em uma determinada raiz, um arquivo por linha com o caminho relativo à raiz e ao comprimento.
Com base na resposta do empilhador. Aqui está uma solução trabalhando em JSP sem bibliotecas externas, para que você possa colocá-lo em praticamente qualquer lugar do seu servidor:
<!DOCTYPE html>
<%@ page session="false" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%!
public List<String> files = new ArrayList<String>();
/**
Fills files array with all sub-files.
*/
public void walk( File root ) {
File[] list = root.listFiles();
if (list == null) return;
for ( File f : list ) {
if ( f.isDirectory() ) {
walk( f );
}
else {
files.add(f.getAbsolutePath());
}
}
}
%>
<%
files.clear();
File jsp = new File(request.getRealPath(request.getServletPath()));
File dir = jsp.getParentFile();
walk(dir);
String prefixPath = dir.getAbsolutePath() + "/";
%>
Então você apenas faz algo como:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>