CodeGym /Cours /JAVA 25 SELF /NIO2: Files, Paths, Files.walk: parcours du système de fi...

NIO2: Files, Paths, Files.walk: parcours du système de fichiers

JAVA 25 SELF
Niveau 39 , Leçon 0
Disponible

1. NIO2: allons plus loin

Nous avons déjà fait connaissance avec NIO2, mais reprenons et approfondissons nos connaissances sur cette bibliothèque utile pour travailler avec les fichiers et les répertoires.

Autrefois, en Java, il n’y avait que la classe File. Elle savait vérifier l’existence d’un fichier, créer et supprimer des fichiers et des dossiers, obtenir la liste des fichiers d’un répertoire. Mais elle présentait de nombreuses limites :

  • peu pratique de travailler avec les chemins, surtout lorsqu’il faut tenir compte de différents systèmes d’exploitation (C:\Users\user\file.txt sous Windows et /home/user/file.txt sous Linux) ;
  • pas de prise en charge correcte des liens symboliques, des permissions et des attributs de fichiers ;
  • des possibilités limitées de parcours de l’arborescence de répertoires ;
  • la gestion des erreurs laissait à désirer.

Avec l’arrivée de NIO2 (New Input/Output, version 2) dans Java 7, la vie des développeurs est devenue plus simple et plus agréable. Désormais, vous disposez de :

  • la classe Path pour travailler aisément avec les chemins vers les fichiers et les dossiers ;
  • la classe Files, qui fournit toutes les opérations de base : lecture, écriture, copie, suppression, obtention d’informations sur les fichiers ;
  • l’interface FileVisitor et des méthodes comme Files.walk, qui permettent de parcourir facilement et avec souplesse le système de fichiers.

Pourquoi est-ce important ?

  • Multiplateforme : Le même code fonctionne sur Windows, Linux, macOS, sans se soucier des séparateurs (/ ou \).
  • Sécurité et simplicité : Plus d’informations sur les erreurs, moins de magie et de mauvaises surprises.
  • Puissance : Vous pouvez traiter d’énormes répertoires et même parcourir récursivement avec filtrage et traitement parallèle.

2. Classes principales : Path et Files

Classe Path

Path — c’est une représentation moderne d’un chemin vers un fichier ou un dossier. Il n’indique pas nécessairement un fichier existant — c’est simplement un chemin avec lequel il est pratique de travailler.

Obtenir un Path

import java.nio.file.Path;
import java.nio.file.Paths;

Path path1 = Paths.get("file.txt"); // chemin relatif
Path path2 = Paths.get("/home/user/file.txt"); // chemin absolu
Path path3 = Path.of("mydir", "subdir", "file.txt"); // depuis Java 11+

Fait : Path ne dépend pas du système d’exploitation. Oubliez la concaténation manuelle de chaînes avec / ou \ !

Conversion en chaîne

System.out.println(path1.toString());

Obtenir le répertoire parent et le nom de fichier

Path parent = path1.getParent(); // peut être null pour les chemins relatifs
Path fileName = path1.getFileName(); // uniquement le nom du fichier

Classe Files

Files — c’est un ensemble de méthodes statiques pour toutes les opérations sur les fichiers et les répertoires :

  • Vérification d’existence : Files.exists(path)
  • Lecture et écriture de fichiers : Files.readAllBytes(path), Files.write(path, bytes)
  • Obtention d’informations : Files.size(path), Files.getLastModifiedTime(path)
  • Copie, suppression, déplacement : Files.copy, Files.delete, Files.move

Exemples :

import java.nio.file.Files;
import java.nio.file.Path;

Path path = Path.of("file.txt");
if (Files.exists(path)) {
    System.out.println("Le fichier existe !");
    System.out.println("Taille: " + Files.size(path) + " octets");
    System.out.println("Dernière modification: " + Files.getLastModifiedTime(path));
} else {
    System.out.println("Fichier introuvable.");
}

3. Parcours du système de fichiers: Files.walk et associés

Le problème de l’ancienne méthode

Dans l’ancien API, pour parcourir tous les fichiers d’un dossier et de ses sous-dossiers, il fallait écrire des fonctions récursives, vérifier manuellement ce qui est fichier ou dossier, et veiller à ne pas tomber dans une récursion infinie. C’était non seulement fastidieux, mais aussi très propice aux erreurs.

Approche moderne : Files.walk

Files.walk(Path start) retourne un Stream<Path> — un flux de tous les fichiers et dossiers à partir du chemin indiqué, y compris tous les sous-répertoires. Désormais, parcourir le système de fichiers revient à travailler avec des streams !

Exemple: afficher tous les fichiers et dossiers

import java.nio.file.*;

try (var paths = Files.walk(Path.of("mydir"))) {
    paths.forEach(System.out::println);
}

Ici, tous les chemins seront affichés : fichiers et dossiers, en partant de mydir.

Exemple: uniquement les fichiers (sans dossiers)

try (var paths = Files.walk(Path.of("mydir"))) {
    paths.filter(Files::isRegularFile)
         .forEach(System.out::println);
}

La méthode Files.isRegularFile(path) renverra true uniquement pour les fichiers ordinaires (ni dossiers, ni liens symboliques).

Exemple: recherche de fichiers par extension

Supposons que nous devions trouver tous les fichiers .txt dans un répertoire et ses sous-répertoires :

try (var paths = Files.walk(Path.of("mydir"))) {
    paths.filter(Files::isRegularFile)
         .filter(path -> path.toString().endsWith(".txt"))
         .forEach(System.out::println);
}

Exemple: calcul de la taille totale de tous les fichiers

long totalSize = 0;
try (var paths = Files.walk(Path.of("mydir"))) {
    totalSize = paths.filter(Files::isRegularFile)
                     .mapToLong(path -> {
                         try {
                             return Files.size(path);
                         } catch (Exception e) {
                             System.err.println("Erreur de lecture de la taille: " + path);
                             return 0L;
                         }
                     })
                     .sum();
}
System.out.println("Taille totale des fichiers: " + totalSize + " octets");

Important !

  • La méthode Files.walk renvoie un stream qu’il faut fermer (elle implémente AutoCloseable). Utilisez donc try-with-resources !
  • Par défaut, la profondeur de parcours est maximale (tous les sous-répertoires). On peut limiter la profondeur : Files.walk(path, maxDepth)

4. Exercices pratiques

Exercice 1: Trouver toutes les images dans un répertoire

Il faut trouver tous les fichiers avec l’extension .jpg, .png, .gif dans le dossier images et afficher leurs noms.

import java.nio.file.*;
import java.util.Set;

Set<String> extensions = Set.of(".jpg", ".png", ".gif");

try (var paths = Files.walk(Path.of("images"))) {
    paths.filter(Files::isRegularFile)
         .filter(path -> {
             String name = path.getFileName().toString().toLowerCase();
             return extensions.stream().anyMatch(name::endsWith);
         })
         .forEach(System.out::println);
}

Exercice 2: Copier tous les fichiers .txt vers un autre dossier

import java.nio.file.*;

Path sourceDir = Path.of("src");
Path destDir = Path.of("dest");

try (var paths = Files.walk(sourceDir)) {
    paths.filter(Files::isRegularFile)
         .filter(path -> path.toString().endsWith(".txt"))
         .forEach(path -> {
             try {
                 Path relative = sourceDir.relativize(path);
                 Path target = destDir.resolve(relative);
                 Files.createDirectories(target.getParent());
                 Files.copy(path, target, StandardCopyOption.REPLACE_EXISTING);
                 System.out.println("Copié: " + path + " -> " + target);
             } catch (Exception e) {
                 System.err.println("Erreur de copie: " + path);
             }
         });
}

Ici, nous conservons la structure des sous-répertoires.

5. Points utiles

Avantages de NIO2

Multiplateforme

Path gère lui-même les séparateurs de dossiers. Votre code fonctionnera de la même manière sur Windows, Linux, macOS.

Traitement en flux

Des méthodes comme Files.walk renvoient un flux (Stream<Path>) que l’on peut filtrer, transformer, collecter — tout ce que sait faire l’API Stream.

Traitement de grands répertoires

L’ancien API pouvait « planter » si les fichiers étaient trop nombreux (par exemple, 100 000 photos). NIO2 gère facilement ces cas, car il ne charge pas tout en mémoire d’un coup.

Prise en charge des liens symboliques, attributs, permissions

Vous pouvez savoir si un chemin est un lien symbolique (Files.isSymbolicLink(path)), obtenir les permissions (Files.getPosixFilePermissions(path)), connaître le propriétaire d’un fichier, et bien plus encore.

Comparaison de l’ancien et du nouvel API

Opération Ancien API (File) Nouvel API (Path, Files)
Vérifier l’existence
file.exists()
Files.exists(path)
Obtenir la taille
file.length()
Files.size(path)
Liste des fichiers d’un dossier
file.listFiles()
Files.list(path)
Parcours récursif Récursion manuelle
Files.walk(path)
Copie de fichier file.renameTo() (peu fiable)
Files.copy(src, dest)
Obtenir l’extension Analyser la chaîne
path.getFileName().toString()
Obtenir le parent
file.getParentFile()
path.getParent()
Gestion des permissions Presque aucune
Files.getPosixFilePermissions(path)

Caractéristiques importantes

Vérification du type de fichier

  • Files.isRegularFile(path) — fichier ordinaire
  • Files.isDirectory(path) — dossier
  • Files.isSymbolicLink(path) — lien symbolique

Travail avec de grands répertoires

  • Évitez de collecter tous les chemins dans une liste : travaillez avec des flux (Stream<Path>) et traitez-les au fil de l’eau.
  • Une fois le travail terminé, le flux doit impérativement être fermé (try-with-resources).

Exceptions

  • Presque toutes les méthodes peuvent lancer une IOException — n’oubliez pas de gérer les erreurs (ou de les propager).

Limiter la profondeur de parcours

try (var paths = Files.walk(Path.of("mydir"), 2)) { // seulement 2 niveaux
    // ...
}

6. Erreurs courantes avec NIO2

Erreur n° 1: vous avez oublié de fermer le stream walk. Si vous n’utilisez pas try-with-resources, vous risquez une fuite de ressources — le flux du système de fichiers restera ouvert. Utilisez toujours la construction try (var paths = Files.walk(...)) { ... }.

Erreur n° 2: vous n’avez pas vérifié que le chemin est un répertoire. Si vous passez à Files.walk un chemin vers un fichier et non vers un dossier, vous pouvez obtenir un comportement inattendu ou une erreur.

Erreur n° 3: exceptions non gérées. Pratiquement toutes les méthodes de NIO2 peuvent lever une IOException. Ne laissez pas ces erreurs sans traitement — affichez au minimum un message à l’utilisateur ou journalisez-les.

Erreur n° 4: confusion avec les séparateurs de chemin. Si vous concaténez manuellement des chemins avec / ou \, ce n’est pas la bonne approche ! Utilisez Path.of(...) ou resolve(...) — elles s’occupent de tout selon l’OS.

Erreur n° 5: tenter de lire un immense répertoire « en mémoire ». Ne collectez pas tous les chemins dans une liste si les fichiers sont très nombreux — travaillez avec des flux (Stream<Path>) et traitez-les au fur et à mesure.

Erreur n° 6: oublier l’aspect multiplateforme. N’inscrivez pas en dur des chemins absolus au style Windows ou Unix. Utilisez Path et des opérations comme resolve/relativize — elles feront le bon choix sur tout OS.

1
Mission
JAVA 25 SELF, niveau 39, leçon 0
Bloqué
Inventaire numérique : aperçu du contenu d'un dossier
Inventaire numérique : aperçu du contenu d'un dossier
1
Mission
JAVA 25 SELF, niveau 39, leçon 0
Bloqué
Recherche du trésor du développeur: trouvez tous les fichiers Java
Recherche du trésor du développeur: trouvez tous les fichiers Java
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION