Listar recursivamente arquivos em Java

258

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

Quintin Par
fonte
2
Acabei de concluir os Resultados do teste que fornecem testes de desempenho para muitas das respostas. Não é de surpreender que todas as respostas baseadas em NIO tenham melhor desempenho. A resposta do commons-io é claramente o pior desempenho, com mais do dobro da duração da execução.
Brett Ryan
2
Java8: Files.walk?
214 Benj

Respostas:

327

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 .

Brett Ryan
fonte
6
Um desses exemplos que podem mostrar a mágica da programação funcional, mesmo para iniciantes.
Johnny
2
Como o desempenho disso se compara aos métodos pré-java 8? Minha passagem de diretório atual é muito lenta e estou procurando por algo que o acelere.
Sridhar Sarnobat
1
Estou escrevendo alguns testes que contêm a maioria das variantes nas respostas fornecidas. Até agora, parece que usar Files.walkcom um fluxo paralelo é o melhor, seguido de perto pelo Files.walkFileTreequal é apenas um pouco mais lento. A resposta aceita usando commons-io é de longe a mais lenta em meus testes, sendo 4 vezes mais lenta.
Brett Ryan
1
@BrettRyan, tentei sua solução, mas recebo uma exceção Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException. Como eu poderia corrigi-lo
Kachna
5
Como obtenho uma lista real de arquivos disso?
thouliha
159

Os FileUtils têm iterateFilese listFilesmé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)

Bozho
fonte
40
FYI / TLDR: se você quiser listar todos os arquivos recursivamente sem filtragem, faça FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE), onde diré um objeto File que aponta para o diretório base.
precisa saber é o seguinte
2
Você pode considerar o uso listFilesAndDirs(), pois listFiles()não retorna pastas vazias.
Schnatterer
1
@ MikeFHay Olhando para o código do FileUtils, acho que seria 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.
Ocramot 23/05
Que tal uma biblioteca nativa JDK? Eu posso implementar isso com facilidade, mas eu seria simplesmente C&P de outros lugares
Christian Bongiorno
1
Estou colocando alguns testes juntos, mas até agora isso parece ter um desempenho quatro vezes mais lento do que o uso de alternativas JDK8 ou JDK7. Os links simbólicos também provam ser problemáticos com essa abordagem, especialmente quando eles são vinculados a diretórios mais altos na árvore, isso faz com que o método nunca retorne, isso pode ser evitado ao manipular o filtro, mas, infelizmente, os links simbólicos em si não são visitados, mesmo que um arquivo.
Brett Ryan
138

// 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:\\" );
    }

}
empilhador
fonte
9
Lembre-se de que para links simbólicos que apontam para um caminho mais alto na hierarquia de caminhos fará com que o método nunca termine. Considere um caminho com um link simbólico que aponte para -> ..
Brett Ryan #
2
Essa é apenas uma implementação ruim do Files.walkFileTree. Eu recomendaria que as pessoas olhassem para FIles.walkFileTree em vez de tentar lançá-lo você mesmo ... Ele está lidando com o problema exato que @BrettRyan apontou.
Tyler Nichols
Obrigado por incluir o arquivo import java.io.File ;. Muitos exemplos esquecem-se de incluir o material do namespace ou mesmo o tipo de dados, tornando o exemplo um ponto de partida em uma viagem de descoberta. Aqui este exemplo está pronto para ser executado. Obrigado.
22419 barrypicker
O caminho pode variar dependendo de onde está o arquivo Filewalker. Use "/", "./"ou "../"para diretório raiz, diretório de trabalho atual e diretório pai, respectivamente
Moses Kirathe
67

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 .

bocejar
fonte
E nunca notifica o fim da caminhada.
Farid
25

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;
}
Petrucio
fonte
simples e limpo
Leo
17

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.

Stefan Schmidt
fonte
6
Na documentação de listFiles(): “Se esse nome de caminho abstrato não indicar um diretório, esse método retornará null.”
hfs 17/11/11
Variante aprimorada estática pública Collection <File> listFileTree (File dir) {if (null == dir ||! Dir.isDirectory ()) {return Collections.emptyList (); } final Set <File> fileTree = new HashSet <File> (); for (Entrada do arquivo: dir.listFiles ()) {if (entry.isFile ()) {fileTree.add (entry); } else {fileTree.addAll (listFileTree (entry)); }} retornar fileTree; }
Ben
Para mim, esta é a resposta mais concisa que é recursiva.
WillieT
14

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);
    }
  }
}
Benroth
fonte
Mas seu algoritmo não pode imprimir com saída recuada. Dirs e arquivos estão bagunçados. Qualquer solução?
Wei
12

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;
}
pstanton
fonte
1
Por favor! deixe o chamador inicializar a lista de arquivos para que não precise verificar sua nulidade a cada vez. Se você deseja criar um segundo método (público) que cria a lista, chama esse método interno e retorna a lista completa.
helios
1
tanto faz. um cheque nulo não é muito caro, conveniência + preferência pessoal, de lado, acho que ele entenderá.
pstanton
Você pode explicar um pouco mais detalhadamente?
uday 30/01
8

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 Fileclasse possui o isDirectory()método).

Michał Niklas
fonte
8

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;
    }
}
chao
fonte
7

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));
Roy Kachouh
fonte
7

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);
        }
    }
}
Ebraheem Alrabee '
fonte
4

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);
        }
    }
}
Sateesh
fonte
3

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());
            }
        }
    }
Rakesh Chaudhari
fonte
2

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 Iterablee 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

cfstras
fonte
1

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);
        }
    }
bobah
fonte
1

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;
    }
user1189332
fonte
1

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());
        }
    }
}
BullyWiiPlaza
fonte
1
    private void fillFilesRecursively(File file, List<File> resultFiles) {
        if (file.isFile()) {
            resultFiles.add(file);
        } else {
            for (File child : file.listFiles()) {
                fillFilesRecursively(child, resultFiles);
            }
        }
    }
legendmohe
fonte
1

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());
        }
    }
}
kanaparthikiran
fonte
0

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.

Ralf R.
fonte
0

Kotlin tem FileTreeWalkpara 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.

Clyde
fonte
0

Outra maneira de fazer isso mesmo que alguém já forneça o Java 8 walk.

Este irá fornecer todos os arquivos recursivamente

  private Stream<File> files(File file) {
    return file.isDirectory()
            ? Arrays.stream(file.listFiles()).flatMap(this::files)
            : Stream.of(file);
}
Michael
fonte
-1

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>
Nux
fonte
1
Embora provavelmente funcione, a pergunta é sobre navegação de arquivos, não renderização de arquivos navegados. Exponha melhor seu algoritmo como tal, não é uma prática recomendada incorporar lógica de negócios em um JSP.
Samuel Kerrien 15/06/2015
Isso depende do que você está fazendo. Em um aplicativo de tamanho empresarial, você está absolutamente certo. Se você só precisa disso como um drop-in para uma lista simples e autônoma, tudo está perfeitamente bem.
Nux