# Java - Manipulation des fichiers et des répertoires avec java.nio

# L'API java.nio.Files

Le package java.nio a été introduit en Java 7 avec la JSR 203 (opens new window) pour remplacer l'API existante java.io.File. Comme on va le voir, la nouvelle API de java.nio.Files (opens new window) rend la manipulation des fichiers et des répertoires très simple. Cet article décrit les méthodes de manipulation des fichiers et des répertoires (ie, création, copie, déplacement, suppression) mais l'API comporte également tout ce qu'il faut pour:

  • la lecture et l'écriture de fichier (synchrone ou asynchrone),
  • le listage du contenu d'un répertoire,
  • la notification des changements dans un répertoire et
  • la manipulation de FileSystem (opens new window).

Sur la partie manipulation de fichier et de répertoire, les principales méthodes de l'API décrites dans cet article sont les suivantes:

Fonctionnalité API
Créer un chemin Paths.get(String first, String... more)
Tester l'existence d'un chemin Files.exists(Path path, LinkOption... options)
Créer un répertoire Files.createDirectory(Path dir, FileAttribute<?>... attrs)
Créer un fichier Files.createFile(Path path, FileAttribute<?>... attrs)
Gestion des attributs PosixFilePermissions.fromString(String perms)
Copier des fichiers Files.copy(Path source, Path target, CopyOption... options)
Déplacer des fichiers Files.move(Path source, Path target, CopyOption... options)
Supprimer des fichiers Files.delete(Path path) et Files.deleteIfExists(Path path)

# La notion de Path

L'interface java.nio.file.Path (opens new window) décrit le chemin vers un élement du système de fichier, que ce soit un fichier, un répertoire ou un lien symbolique. Un objet Path peut représenter ces éléments sous forme de chemin absolu ou relatif. La classe Paths (opens new window) permet de créer rapidement un objet Path à partir des éléments du chemin via la fonction Paths.get:



 









@Test
public void getPath() {
  Path someFile = Paths.get("/tmp", "subdirectory", "file.txt");
  assertThat(someFile.toString()).isEqualTo("/tmp/subdirectory/file.txt");
  assertThat(someFile.getParent().toString()).isEqualTo("/tmp/subdirectory");
  assertThat(someFile.getFileName().toString()).isEqualTo("file.txt");
  assertTrue(someFile.isAbsolute());
  assertTrue(someFile.startsWith("/tmp"));
  assertTrue(someFile.endsWith("file.txt"));
  assertTrue(someFile.startsWith(Paths.get("/tmp")));
  assertTrue(someFile.endsWith(Paths.get("file.txt")));
}
1
2
3
4
5
6
7
8
9
10
11

Comme on peut le voir ci-dessus, une fois l'objet Path expose également plusieurs fonctions pour interroger le contenu du chemin.

# resolve: combiner des Path

La méthode resolve permet de combiner un Path à un autre, à condition que ce dernier soit relatif. Si ce n'est pas le cas, le Path donné en paramètre à la méthode resolve est retourné.




 




@Test
public void resolve() {
  Path someDirectory = Paths.get("/tmp");
  Path someFile = someDirectory.resolve("subdirectory").resolve("file.txt");
  assertThat(someFile.toString()).isEqualTo("/tmp/subdirectory/file.txt");
  assertThat(someFile.getParent().toString()).isEqualTo("/tmp/subdirectory");
  assertThat(someFile.getFileName().toString()).isEqualTo("file.txt");
}
1
2
3
4
5
6
7

# relativize: chemin entre deux Path

La méthode relativize permet d'obtenir le Path relative entre deux Path, c'est à dire le chemin à traverser pour aller de l'un à l'autre:





 

 


@Test
public void relativize() {
  Path someDir = Paths.get("/tmp", "dir");
  Path someOtherDir = Paths.get("/tmp", "otherdir");
  Path fromSomeDirToSomeOtherDir = someDir.relativize(someOtherDir);
  assertThat(fromSomeDirToSomeOtherDir.toString()).isEqualTo("../otherdir");
  Path fromSomeOtherDirToSomeDir = someOtherDir.relativize(someDir);
  assertThat(fromSomeOtherDirToSomeDir.toString()).isEqualTo("../dir");
}
1
2
3
4
5
6
7
8

WARNING

Assez logiquement, cette méthode n'est pas symétrique: le chemin relatif entre deux Path n'est pas le même suivant qu'un Path est choisi comme source ou destination.

# normalize: nettoyer un Path

Enfin la méthode normalize permet de simplifier un Path donné en éliminant les éléments de type . et ...





 


@Test
public void normalize() {
  Path someDir = Paths.get("/tmp", "dir", "..", "otherdir", ".");
  assertThat(someDir.toString()).isEqualTo("/tmp/dir/../otherdir/.");
  Path someDirNormalized = someDir.normalize();
  assertThat(someDirNormalized.toString()).isEqualTo("/tmp/otherdir");
}
1
2
3
4
5
6

# Tester l'existance d'un fichier ou d'un répertoire

Il est très facile de tester l'existence physique d'un objet Path via la méthode statique java.nio.Files.exists:



 


@Test
public void testIfDirectoryOrFileExists(@TempDir Path someDirectory) {
  boolean directoryExist = Files.exists(someDirectory);
  assertTrue(directoryExist);
}
1
2
3
4

# Créer un répertoire

La méthode java.nio.Files.createDirectory permet de créer simplement le répertoire qui correspond à un Path donné:







 







@Test
public void createDirectory(@TempDir Path someDirectory) {
  final String subdirectoryName = "subdirectory";
  Path someSubdirectory = someDirectory.resolve(subdirectoryName);
  assertFalse(Files.exists(someSubdirectory));
  try {
    Files.createDirectory(someSubdirectory);
  } catch (FileAlreadyExistsException e) {
    LOG.error("The directory already exists", e);
  } catch (IOException e) {
    LOG.error("The directory can't be created", e);
  }
  assertTrue(Files.exists(someSubdirectory));
}
1
2
3
4
5
6
7
8
9
10
11
12
13

WARNING

La méthode java.nio.Files.createDirectory renvoie une exception FileAlreadyExistsException si le Path donné existe déjà. Pour créer un répertoire seulement s'il n'existe pas, il n'y a pas de méthode dédiée. Une solution consiste donc à tester l'existance du répertoire avant de le créer.








 





@Test
public void createDirectoryIfNotExists(@TempDir Path someDirectory) {
  final String subdirectoryName = "subdirectory";
  Path someSubdirectory = someDirectory.resolve(subdirectoryName);
  assertFalse(Files.exists(someSubdirectory));
  try {
    if (!Files.exists(someSubdirectory)) {
      Files.createDirectory(someSubdirectory);
    }
  } catch (IOException e) {
    LOG.error("The directory can't be created", e);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# Créer un fichier

Créer un fichier est aussi simple que créer un répertoire avec la méthode java.nio.Files.createFile. Là encore, la méthode renvoie une exception si le fichier existe déjà.







 







@Test
public void createFile(@TempDir Path someDirectory) {
  final String someFilename = "file.txt";
  Path someFile = someDirectory.resolve(someFilename);
  assertFalse(Files.exists(someFile));
  try {
    Files.createFile(someFile);
  } catch (FileAlreadyExistsException e) {
    LOG.error("The file already exists", e);
  } catch (IOException e) {
    LOG.error("The file can't be created", e);
  }
  assertTrue(Files.exists(someFile));
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# Gestion des attributs à la création

Pour la création d'un fichier ou d'un répertoire, il est possible de spécifier les attributs du système de fichier à utiliser. Sur les systèmes POSIX, cela revient à donner les permissions à appliquer lors de la création du fichier. Ces permissions sont définies par la classe PosixFilePermission. Il est facile de créer les permissions souhaitées via la méthode statique PosixFilePermissions.fromString



 
 
 






@Test
public void createFileWithAttributes(@TempDir Path someDirectory) throws IOException {
  Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rw-r--r--");
  FileAttribute<Set<PosixFilePermission>> attributes =
      PosixFilePermissions.asFileAttribute(permissions);
  Path someFile = someDirectory.resolve("file.txt");
  Files.createFile(someFile, attributes);
  assertTrue(Files.isReadable(someFile));
  assertTrue(Files.isWritable(someFile));
  assertFalse(Files.isExecutable(someFile));
}
1
2
3
4
5
6
7
8
9
10

TIP

Dans l'exemple précédent, le fichier a bien les droits d'écriture puisque ceux-ci ont été donnés au propriétaire du fichier, ie l'utilisateur qui a lancé le programme. En revanche, le fichier n'est pas considéré comme exécutable puisque personne n'a les droits d'exécution.

# Copier un fichier

La copie d'un fichier passe par la méthode Files.copy qui prends en paramètre les Path source et destination de la copie:










 







@Test
public void copyFile(@TempDir Path someDirectory) throws IOException {
  Path someInputFile = someDirectory.resolve("file.txt");
  Files.createFile(someInputFile);
  assertTrue(Files.exists(someInputFile));
  Path someOutputFile =
      Files.createDirectory(someDirectory.resolve("output")).resolve("file2.txt");
  assertFalse(Files.exists(someOutputFile));
  try {
    Files.copy(someInputFile, someOutputFile);
  } catch (FileAlreadyExistsException e) {
    LOG.error("The directory already exists", e);
  } catch (IOException e) {
    LOG.error("The directory can't be created", e);
  }
  assertTrue(Files.exists(someOutputFile));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

WARNING

Par défaut, Files.copy génère une exception FileAlreadyExistsException si le Path de destination existe déjà. Il est cependant possible de demander à remplacer la destination plutôt que de générer une exception dans ce cas là. Pour celà, il faut spécifier l'option StandardCopyOption.REPLACE_EXISTING lors de la copie:











 





@Test
public void copyFileReplaceIfExists(@TempDir Path someDirectory) throws IOException {
  Path someInputFile = someDirectory.resolve("file.txt");
  Files.createFile(someInputFile);
  assertTrue(Files.exists(someInputFile));
  Path someOutputFile =
      Files.createDirectory(someDirectory.resolve("output")).resolve("file2.txt");
  Files.createFile(someOutputFile);
  assertTrue(Files.exists(someOutputFile));
  try {
    Files.copy(someInputFile, someOutputFile, StandardCopyOption.REPLACE_EXISTING);
  } catch (IOException e) {
    LOG.error("The directory can't be created", e);
  }
  assertTrue(Files.exists(someOutputFile));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Déplacer un fichier

Pour déplacer un fichier, il faut utiliser la méthode Files.move. Tout comme File.copy, cette méthode prends en paramètre des Path sources et destinations.










 








@Test
public void moveFile(@TempDir Path someDirectory) throws IOException {
  Path someInputFile = someDirectory.resolve("file.txt");
  Files.createFile(someInputFile);
  assertTrue(Files.exists(someInputFile));
  Path someOutputFile =
      Files.createDirectory(someDirectory.resolve("output")).resolve("file.txt");
  assertFalse(Files.exists(someOutputFile));
  try {
    Files.move(someInputFile, someOutputFile);
  } catch (FileAlreadyExistsException e) {
    LOG.error("The file already exists", e);
  } catch (IOException e) {
    LOG.error("The file can't be created", e);
  }
  assertFalse(Files.exists(someInputFile));
  assertTrue(Files.exists(someOutputFile));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Là encore, par défaut, cette méthode génère une exception si le Path de destination existe déjà. Ce comportement se change en passant l'option StandardCopyOption.REPLACE_EXISTING en dernier paramètre de la méthode:











 






@Test
public void moveFileReplaceIfExists(@TempDir Path someDirectory) throws IOException {
  Path someInputFile = someDirectory.resolve("file.txt");
  Files.createFile(someInputFile);
  assertTrue(Files.exists(someInputFile));
  Path someOutputFile =
      Files.createDirectory(someDirectory.resolve("output")).resolve("file.txt");
  Files.createFile(someOutputFile);
  assertTrue(Files.exists(someOutputFile));
  try {
    Files.move(someInputFile, someOutputFile, StandardCopyOption.REPLACE_EXISTING);
  } catch (IOException e) {
    LOG.error("The file can't be created", e);
  }
  assertFalse(Files.exists(someInputFile));
  assertTrue(Files.exists(someOutputFile));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Supprimer un fichier

Il suffit d'appeler la méthode Files.delete pour supprimer un fichier:







 







@Test
public void deleteFile(@TempDir Path someDirectory) throws IOException {
  Path someFile = someDirectory.resolve("file.txt");
  Files.createFile(someFile);
  assertTrue(Files.exists(someFile));
  try {
    Files.delete(someFile);
  } catch (NoSuchFileException e) {
    LOG.error("The file doesn't exists", e);
  } catch (IOException e) {
    LOG.error("The file can't be deleted", e);
  }
  assertFalse(Files.exists(someFile));
}
1
2
3
4
5
6
7
8
9
10
11
12
13

TIP

Dans le cas où le fichier à supprimer n'existe pas, Files.delete génère une exception de type NoSuchFileException. Pour ignorer ces cas, il faut utiliser la méthode deleteFileIfExists.






 





@Test
public void deleteFileIfExists(@TempDir Path someDirectory) throws IOException {
  Path someFile = someDirectory.resolve("file.txt");
  assertFalse(Files.exists(someFile));
  try {
    Files.deleteIfExists(someFile);
  } catch (IOException e) {
    LOG.error("The file can't be deleted", e);
  }
  assertFalse(Files.exists(someFile));
}
1
2
3
4
5
6
7
8
9
10
Dernière mise à jour: 1/1/2020, 9:50:45 AM