Рекурсивно вывести список всех файлов в каталоге с помощью nio.file.DirectoryStream;

java file nio directorystream

72019 просмотра

8 ответа

Я хочу перечислить все файлы в указанном каталоге и подкаталогах в этом каталоге. Каталоги не должны быть перечислены.

Мой текущий код ниже. Он не работает должным образом, так как перечисляет только файлы и каталоги в указанном каталоге.

Как я могу это исправить?

final List<Path> files = new ArrayList<>();

Path path = Paths.get("C:\\Users\\Danny\\Documents\\workspace\\Test\\bin\\SomeFiles");
try
{
  DirectoryStream<Path> stream;
  stream = Files.newDirectoryStream(path);
  for (Path entry : stream)
  {
    files.add(entry);
  }
  stream.close();
}
catch (IOException e)
{
  e.printStackTrace();
}

for (Path entry: files)
{
  System.out.println(entry.toString());
}
Автор: Danny Rancher Источник Размещён: 12.11.2019 09:25

Ответы (8)


63 плюса

Java 8 предоставляет хороший способ для этого:

Files.walk(path)

Этот метод возвращает Stream<Path>.

Автор: Vladimir Petrakovich Размещён: 23.04.2016 07:06

31 плюса

Создайте метод, который будет вызывать себя, если следующий элемент является каталогом

void listFiles(Path path) throws IOException {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
        for (Path entry : stream) {
            if (Files.isDirectory(entry)) {
                listFiles(entry);
            }
            files.add(entry);
        }
    }
}
Автор: Evgeniy Dorofeev Размещён: 08.01.2014 04:56

26 плюса

Проверьте FileVisitor , очень аккуратно.

 Path path= Paths.get("C:\\Users\\Danny\\Documents\\workspace\\Test\\bin\\SomeFiles");
 final List<Path> files=new ArrayList<>();
 try {
    Files.walkFileTree(path, new SimpleFileVisitor<Path>(){
     @Override
     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
          if(!attrs.isDirectory()){
               files.add(file);
          }
          return FileVisitResult.CONTINUE;
      }
     });
 } catch (IOException e) {
      e.printStackTrace();
 }
Автор: Adi Размещён: 08.01.2014 05:05

6 плюса

Если вы хотите избежать рекурсивного вызова функции и иметь список файлов, который является переменной-членом, вы можете использовать стек:

private List<Path> listFiles(Path path) throws IOException {
    Deque<Path> stack = new ArrayDeque<Path>();
    final List<Path> files = new LinkedList<>();

    stack.push(path);

    while (!stack.isEmpty()) {
        DirectoryStream<Path> stream = Files.newDirectoryStream(stack.pop());
        for (Path entry : stream) {
            if (Files.isDirectory(entry)) {
                stack.push(entry);
            }
            else {
                files.add(entry);
            }
        }
        stream.close();
    }

    return files;
}
Автор: Duarte Meneses Размещён: 07.05.2014 01:34

2 плюса

Используя Rx Java, требование может быть решено несколькими способами, в то же время придерживаясь использования DirectoryStream из JDK.

Следующие комбинации дадут вам желаемый эффект, я бы объяснил их по порядку:

Подход 1 . Рекурсивный подход с использованием операторов flatMap () и defer ()

Подход 2 . Рекурсивный подход с использованием операторов flatMap () и fromCallable

Примечание. Если вы замените использование flatMap () на concatMap () , навигация по дереву каталогов обязательно произойдет в режиме поиска в глубину (DFS). При использовании flatMap () эффект DFS не гарантируется.

Подход 1: Использование flatMap () и defer ()

   private Observable<Path> recursiveFileSystemNavigation_Using_Defer(Path dir) {
       return Observable.<Path>defer(() -> {
            //
            // try-resource block
            //
            try(DirectoryStream<Path> children = Files.newDirectoryStream(dir))
            {
                //This intermediate storage is required because DirectoryStream can't be navigated more than once.
                List<Path> subfolders = Observable.<Path>fromIterable(children)
                                                        .toList()
                                                        .blockingGet();


                return Observable.<Path>fromIterable(subfolders)
                        /* Line X */    .flatMap(p -> !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_Using_Defer(p), Runtime.getRuntime().availableProcessors());

                //      /* Line Y */  .concatMap(p -> !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_Using_Defer(p));

            } catch (IOException e) {
                /*
                 This catch block is required even though DirectoryStream is  Closeable
                 resource. Reason is that .close() call on a DirectoryStream throws a 
                 checked exception.
                */
                return Observable.<Path>empty();
            }
       });
    }

Этот подход находит детей заданного каталога, а затем отправляет детей в виде наблюдаемых. Если дочерний файл является файлом, он будет сразу же доступен подписчику, иначе flatMap () в строке X будет вызывать метод, рекурсивно передавая каждый подкаталог в качестве аргумента. Для каждого такого подкаталога flatmap будет внутренне подписываться на своих детей одновременно. Это похоже на цепную реакцию, которую нужно контролировать.

Поэтому использование Runtime.getRuntime (). AvailableProcessors () устанавливает максимальный уровень параллелизма для flatmap () и предотвращает одновременную подписку на все подпапки. Не задавая уровень параллелизма, представьте, что произойдет, если в папке будет 1000 дочерних элементов.

Использование defer () предотвращает преждевременное создание DirectoryStream и гарантирует, что оно произойдет только тогда, когда будет создана настоящая подписка для поиска его подпапок.

Наконец, метод возвращает Observable , чтобы клиент мог подписаться и сделать что-то полезное с результатами, как показано ниже:

//
// Using the defer() based approach
//
recursiveDirNavigation.recursiveFileSystemNavigation_Using_Defer(startingDir)
                    .subscribeOn(Schedulers.io())
                    .observeOn(Schedulers.from(Executors.newFixedThreadPool(1)))
                    .subscribe(p -> System.out.println(p.toUri()));

Недостаток использования defer () состоит в том, что он не справляется с проверенными исключениями, если его функция аргумента генерирует проверенное исключение. Поэтому, хотя DirectoryStream (который реализует Closeable) был создан в блоке try-resource, нам все равно пришлось перехватить IOException, поскольку автоматическое закрытие DirectoryStream генерирует это проверенное исключение.

При использовании стиля на основе Rx использование блоков catch () для обработки ошибок звучит немного странно, потому что четные ошибки отправляются как события в реактивном программировании. Так почему бы нам не использовать оператор, который выставляет такие ошибки как события.

Лучшая альтернатива названа fromCallable () была добавлена в Rx Java 2.x . Второй подход показывает использование этого.

Подход 2. Использование операторов flatMap () и fromCallable

Этот подход использует оператор fromCallable (), который принимает Callable в качестве аргумента. Так как мы хотим рекурсивный подход, ожидаемый результат от этого вызываемого объекта - Observable дочерних элементов данной папки. Поскольку мы хотим, чтобы подписчик получал результаты, когда они доступны, нам необходимо вернуть Observable из этого метода. Так как результат внутреннего вызова - это наблюдаемый список детей, чистый эффект - это наблюдаемая из наблюдаемых.

   private Observable<Observable<Path>> recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(Path dir) {
       /*
        * fromCallable() takes a Callable argument. In this case the callbale's return value itself is 
        * a list of sub-paths therefore the overall return value of this method is Observable<Observable<Path>>
        * 
        * While subscribing the final results, we'd flatten this return value.
        * 
        * Benefit of using fromCallable() is that it elegantly catches the checked exceptions thrown 
        * during the callable's call and exposes that via onError() operator chain if you need. 
        * 
        * Defer() operator does not give that flexibility and you have to explicitly catch and handle appropriately.   
        */
       return Observable.<Observable<Path>> fromCallable(() -> traverse(dir))
                                        .onErrorReturnItem(Observable.<Path>empty());

    }

    private Observable<Path> traverse(Path dir) throws IOException {
        //
        // try-resource block
        //
        try(DirectoryStream<Path> children = Files.newDirectoryStream(dir))
        {
            //This intermediate storage is required because DirectoryStream can't be navigated more than once.
            List<Path> subfolders = Observable.<Path>fromIterable(children)
                                                    .toList()
                                                    .blockingGet();

            return Observable.<Path>fromIterable(subfolders)
                    /* Line X */    .flatMap(p -> ( !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(p).blockingSingle())
                                             ,Runtime.getRuntime().availableProcessors());

            //      /* Line Y */  .concatMap(p -> ( !isFolder(p) ? Observable.<Path> just(p) : recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(p).blockingSingle() ));

        }
    }

После этого подписчик должен будет сгладить поток результатов, как показано ниже:

//
// Using the fromCallable() based approach
//
recursiveDirNavigation.recursiveFileSystemNavigation_WithoutExplicitCatchBlock_UsingFromCallable(startingDir)
                        .subscribeOn(Schedulers.io())
                        .flatMap(p -> p)
                        .observeOn(Schedulers.from(Executors.newFixedThreadPool(1)))
                        .subscribe(filePath -> System.out.println(filePath.toUri()));

В методе traverse () почему строка X использует блокировку Get

Поскольку рекурсивная функция возвращает Observable , но для плоской карты в этой строке требуется Observable, на которую можно подписаться.

Строка Y в обоих подходах использует concatMap ()

Потому что concatMap () можно удобно использовать, если мы не хотим параллелизма во внутренних подписках, сделанных flatmap ().

В обоих подходах реализация метода isFolder выглядит следующим образом:

private boolean isFolder(Path p){
    if(p.toFile().isFile()){
        return false;
    }

    return true;
}

Maven координаты для Java RX 2.0

<dependency>
    <groupId>io.reactivex.rxjava2</groupId>
    <artifactId>rxjava</artifactId>
    <version>2.0.3</version>
</dependency>

Импорт в файл Java

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.concurrent.Executors;
import io.reactivex.Observable;
import io.reactivex.schedulers.Schedulers;
Автор: NitinS Размещён: 21.12.2016 08:06

2 плюса

Это самая короткая реализация, которую я придумал:

final List<Path> files = new ArrayList<>();
Path path = Paths.get("C:\\Users\\Danny\\Documents\\workspace\\Test\\bin\\SomeFiles");
try {
    Files.walk(path).forEach(entry -> list.add(entry));
} catch (IOException e) {
    e.printStackTrack();
}
Автор: REjsmont Размещён: 25.04.2018 08:02

0 плюса

Попробуйте это .. он проходит через каждую папку и распечатывает обе папки, а также файлы: -

public static void traverseDir(Path path) {
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
        for (Path entry : stream) {
            if (Files.isDirectory(entry)) {
                System.out.println("Sub-Folder Name : " + entry.toString());
                traverseDir(entry);
            } else {
                System.out.println("\tFile Name : " + entry.toString());
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
Автор: Yogesh Deshmukh Размещён: 09.02.2017 08:08

-1 плюса

Попробуйте: вы получите список каталогов и путь к подкаталогу; Там может быть неограниченный подкаталог, попробуйте использовать recursiveпроцесс.

public class DriectoryFileFilter {
    private List<String> filePathList = new ArrayList<String>();

    public List<String> read(File file) {
        if (file.isFile()) {
            filePathList.add(file.getAbsolutePath());
        } else if (file.isDirectory()) {
            File[] listOfFiles = file.listFiles();
            if (listOfFiles != null) {
                for (int i = 0; i < listOfFiles.length; i++){
                    read(listOfFiles[i]);
                }
            } else {
                System.out.println("[ACCESS DENIED]");
            }
        }
        return filePathList;
    }
}
Автор: Zaw Than oo Размещён: 08.01.2014 05:44
Вопросы из категории :
32x32