Liste récursive de tous les fichiers dans un répertoire en utilisant nio.fichier.DirectoryStream;
je veux lister tous les fichiers dans le répertoire spécifié et les sous-répertoires dans ce répertoire. Aucun répertoire ne doit être listé.
mon code actuel est ci-dessous. Elle ne fonctionne pas correctement car il ne répertorie que les fichiers et répertoires dans le répertoire spécifié.
Comment puis-je résoudre ce problème?
final List<Path> files = new ArrayList<>();
Path path = Paths.get("C:UsersDannyDocumentsworkspaceTestbinSomeFiles");
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());
}
salutations.
8 réponses
Java 8 fournit une bonne façon pour cela:
Files.walk(path)
Cette méthode retourne Stream<Path>
.
créer une méthode qui s'appellera elle-même si un prochain élément est directory
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);
}
}
}
Case FileVisitor, très soigné.
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();
}
si vous voulez éviter que la fonction s'appelle de façon récursive et que vous ayez une liste de fichiers qui est une variable membre, vous pouvez utiliser une pile:
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;
}
en utilisant Rx Java, l'exigence peut être résolue de plusieurs façons tout en restant à L'usage de DirectoryStream de JDK.
les combinaisons suivantes vous donneront l'effet désiré, je les expliquerais dans l'ordre:
approche 1. Une approche récursive utilisant les opérateurs flatMap() et defer ()
approche 2. Une approche récursive utilisant flatMap () et des opérateurs appelables
Remarque: Si vous remplacer l'utilisation de flatMap ()concatMap (), la navigation dans l'arborescence des répertoires se fera nécessairement d'une manière DFS (depth-first-search). Avec flatMap (), L'effet DFS n'est pas garanti.
méthode 1: Utilisation de flatMap() et de reporter()
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();
}
});
}
cette approche consiste à trouver les enfants d'un répertoire donné puis à les émettre comme Observables. Si un enfant est un fichier, il sera immédiatement disponible à un abonné d'autre flatMap() on Ligne X va invoquer la méthode en passant récursivement chaque sous-répertoire comme argument. Pour chacun de ces sous-répertoires, flatmap s'abonnera en interne à leurs enfants en même temps. C'est comme une réaction en chaîne qui doit être contrôlé.
Donc Runtime.getRuntime ().availableProcessors() configure niveau maximal de concurrence pour flatmap() et l'empêche de s'abonner à tous les sous-dossiers dans le même temps. Sans mettre le niveau de simultanéité, imaginez ce qui se passera quand un dossier aura 1000 enfants.
reporter() empêche la création d'un DirectoryStream prématurément et assure que cela ne se produira que lorsqu'un véritable abonnement pour trouver ses sous-dossiers est fait.
Enfin, la méthode renvoie un Observables < Chemin D'Accès > pour qu'un client puisse s'abonner et faire quelque chose d'utile avec les résultats affichés ci-dessous:
//
// 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()));
inconvénient d'utiliser defer () est qu'il ne traite pas correctement les exceptions vérifiées si sa fonction d'argument lance une exception vérifiée. Par conséquent, même si DirectoryStream (qui implémente Fermer) a été créé dans un bloc try-resource, nous avons quand même dû attraper le IOException parce que la fermeture automatique d'un DirectoryStream déclenche cette exception cochée.
tout en utilisant le style basé sur Rx, l'utilisation des blocs catch() pour la gestion des erreurs semble un peu bizarre car même les erreurs sont envoyées comme des événements dans la programmation réactive. Alors pourquoi ne pas utiliser un opérateur qui expose de telles erreurs comme des événements.
Une meilleure alternative nommée fromCallable () a été ajouté à Rx Java 2.x. 2ème approche montre l'utilisation de l'informatique.
approche 2. Utiliser flatMap () et fromCallable operators
Cette approche utilise fromCallable () opérateur qui prend un appelable comme argument. Puisque nous voulons une approche récursive, le résultat attendu de cette appelable est Observable des enfants de ce dossier. Puisque nous voulons qu'un abonné reçoive des résultats lorsqu'ils sont disponibles, nous devons retourner un Observable de cette méthode. Puisque le résultat de l'appelable intérieur est une liste Observable d'enfants, l'effet net est un Observable des Observables.
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() ));
}
}
Un abonné aura alors besoin d'aplatir les résultats de flux comme indiqué ci-dessous:
//
// 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()));
dans la méthode traverse (), pourquoi la ligne X utilise-t-elle le blocage Get
parce que la fonction récursive renvoie un < Observable > Observable, mais flatmap à cette ligne a besoin d'un Observable pour s'abonner.
la ligne Y dans les deux approches utilise concatMap ()
parce que concatMap() peut être utilisé confortablement si nous ne voulons pas de parallélisme pendant innner abonnements souscrits par flatmap ().
dans les deux approches, la mise en oeuvre de la méthode isFolder ressemble à ci-dessous:
private boolean isFolder(Path p){
if(p.toFile().isFile()){
return false;
}
return true;
}
coordonnées Maven pour Java RX 2.0
<dependency>
<groupId>io.reactivex.rxjava2</groupId>
<artifactId>rxjava</artifactId>
<version>2.0.3</version>
</dependency>
des Importations dans le fichier 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;
C'est le plus court de mise en œuvre, je suis venu avec:
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();
}
Essayez ceci ..il traverse tous les dossier et imprimer les dossiers ainsi que les fichiers:-
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();
}
}
Try: Vous obtiendrez une liste de chemin de répertoire et de sous-répertoire;
Il peut y avoir un sous-répertoire illimité, essayez d'utiliser recursive
processus.
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;
}
}