# 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")));
}
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");
}
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");
}
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");
}
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);
}
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));
}
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);
}
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
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));
}
2
3
4
5
6
7
8
9
10