Introduction à l'API de fichier Java NIO2

1. Vue d'ensemble

Dans cet article, nous allons nous concentrer sur les nouvelles API d'E / S de la plateforme Java - NIO2 - pour effectuer des manipulations de fichiers de base .

Les API de fichiers dans NIO2 constituent l'un des nouveaux domaines fonctionnels majeurs de la plate-forme Java livrée avec Java 7, en particulier un sous-ensemble de la nouvelle API du système de fichiers aux côtés des API Path.

2. Configuration

Configurer votre projet pour utiliser les API de fichier est juste une question de faire cette importation:

import java.nio.file.*;

Étant donné que les exemples de code de cet article seront probablement exécutés dans différents environnements, prenons en compte le répertoire personnel de l'utilisateur, qui sera valide sur tous les systèmes d'exploitation:

private static String HOME = System.getProperty("user.home");

La classe Files est l'un des principaux points d'entrée du package java.nio.file . Cette classe offre un ensemble complet d'API pour la lecture, l'écriture et la manipulation de fichiers et de répertoires. Les méthodes de classe Files fonctionnent sur des instances d' objets Path .

3. Vérification d'un fichier ou d'un répertoire

Nous pouvons avoir une instance Path représentant un fichier ou un répertoire sur le système de fichiers. Que ce fichier ou répertoire vers lequel il pointe existe ou non, est accessible ou non peut être confirmé par une opération de fichier.

Par souci de simplicité, chaque fois que nous utiliserons le terme fichier , nous ferons référence à la fois aux fichiers et aux répertoires, sauf indication contraire explicite.

Pour vérifier si un fichier existe, nous utilisons le existe API:

@Test public void givenExistentPath_whenConfirmsFileExists_thenCorrect() { Path p = Paths.get(HOME); assertTrue(Files.exists(p)); }

Pour vérifier qu'un fichier n'existe pas, nous utilisons l' API notExists :

@Test public void givenNonexistentPath_whenConfirmsFileNotExists_thenCorrect() { Path p = Paths.get(HOME + "/inexistent_file.txt"); assertTrue(Files.notExists(p)); }

Nous pouvons également vérifier si un fichier est un fichier régulier comme myfile.txt ou n'est qu'un répertoire, nous utilisons l' API isRegularFile :

@Test public void givenDirPath_whenConfirmsNotRegularFile_thenCorrect() { Path p = Paths.get(HOME); assertFalse(Files.isRegularFile(p)); }

Il existe également des méthodes statiques pour vérifier les autorisations de fichier. Pour vérifier si un fichier est lisible, nous utilisons l' API isReadable :

@Test public void givenExistentDirPath_whenConfirmsReadable_thenCorrect() { Path p = Paths.get(HOME); assertTrue(Files.isReadable(p)); }

Pour vérifier s'il est accessible en écriture, nous utilisons l' API isWritable :

@Test public void givenExistentDirPath_whenConfirmsWritable_thenCorrect() { Path p = Paths.get(HOME); assertTrue(Files.isWritable(p)); }

De même, pour vérifier s'il est exécutable:

@Test public void givenExistentDirPath_whenConfirmsExecutable_thenCorrect() { Path p = Paths.get(HOME); assertTrue(Files.isExecutable(p)); }

Lorsque nous avons deux chemins, nous pouvons vérifier s'ils pointent tous les deux vers le même fichier sur le système de fichiers sous-jacent:

@Test public void givenSameFilePaths_whenConfirmsIsSame_thenCorrect() { Path p1 = Paths.get(HOME); Path p2 = Paths.get(HOME); assertTrue(Files.isSameFile(p1, p2)); }

4. Création de fichiers

L'API du système de fichiers fournit des opérations sur une seule ligne pour la création de fichiers. Pour créer un fichier normal, nous utilisons l' API createFile et lui passons un objet Path représentant le fichier que nous voulons créer.

Tous les éléments de nom dans le chemin doivent exister, à part le nom du fichier, sinon, nous obtiendrons une IOException:

@Test public void givenFilePath_whenCreatesNewFile_thenCorrect() { String fileName = "myfile_" + UUID.randomUUID().toString() + ".txt"; Path p = Paths.get(HOME + "/" + fileName); assertFalse(Files.exists(p)); Files.createFile(p); assertTrue(Files.exists(p)); }

Dans le test ci-dessus, lorsque nous vérifions le chemin pour la première fois, il est inexistant, puis après l' opération createFile , il s'avère qu'il existe.

Pour créer un répertoire, nous utilisons l' API createDirectory :

@Test public void givenDirPath_whenCreatesNewDir_thenCorrect() { String dirName = "myDir_" + UUID.randomUUID().toString(); Path p = Paths.get(HOME + "/" + dirName); assertFalse(Files.exists(p)); Files.createDirectory(p); assertTrue(Files.exists(p)); assertFalse(Files.isRegularFile(p)); assertTrue(Files.isDirectory(p)); }

Cette opération nécessite que tous les éléments de nom du chemin existent, sinon, nous obtenons également une IOException :

@Test(expected = NoSuchFileException.class) public void givenDirPath_whenFailsToCreateRecursively_thenCorrect() { String dirName = "myDir_" + UUID.randomUUID().toString() + "/subdir"; Path p = Paths.get(HOME + "/" + dirName); assertFalse(Files.exists(p)); Files.createDirectory(p); }

Cependant, si nous souhaitons créer une hiérarchie de répertoires avec un seul appel, nous utilisons la méthode createDirectories . Contrairement à l'opération précédente, lorsqu'elle rencontre des éléments de nom manquants dans le chemin, elle ne lève pas d' exception IOException , elle les crée de manière récursive menant au dernier élément:

@Test public void givenDirPath_whenCreatesRecursively_thenCorrect() { Path dir = Paths.get( HOME + "/myDir_" + UUID.randomUUID().toString()); Path subdir = dir.resolve("subdir"); assertFalse(Files.exists(dir)); assertFalse(Files.exists(subdir)); Files.createDirectories(subdir); assertTrue(Files.exists(dir)); assertTrue(Files.exists(subdir)); }

5. Création de fichiers temporaires

De nombreuses applications créent une trace des fichiers temporaires dans le système de fichiers lors de leur exécution. En conséquence, la plupart des systèmes de fichiers ont un répertoire dédié pour stocker les fichiers temporaires générés par ces applications.

La nouvelle API du système de fichiers fournit des opérations spécifiques à cet effet. L' API createTempFile effectue cette opération. Il prend un objet chemin, un préfixe de fichier et un suffixe de fichier:

@Test public void givenFilePath_whenCreatesTempFile_thenCorrect() { String prefix = "log_"; String suffix = ".txt"; Path p = Paths.get(HOME + "/"); Files.createTempFile(p, prefix, suffix); assertTrue(Files.exists(p)); }

Ces paramètres sont suffisants pour les exigences qui nécessitent cette opération. Cependant, si vous devez spécifier des attributs spécifiques du fichier, il existe un quatrième paramètre d'arguments de variable.

Le test ci-dessus crée un fichier temporaire dans le répertoire HOME , en pré-attente et en ajoutant respectivement les chaînes de préfixe et de suffixe fournies. Nous finirons avec un nom de fichier comme log_8821081429012075286.txt . La longue chaîne numérique est générée par le système.

Cependant, si nous ne fournissons pas de préfixe et de suffixe, le nom du fichier n'inclura que la longue chaîne numérique et une extension .tmp par défaut :

@Test public void givenPath_whenCreatesTempFileWithDefaults_thenCorrect() { Path p = Paths.get(HOME + "/"); Files.createTempFile(p, null, null); assertTrue(Files.exists(p)); }

L'opération ci-dessus crée un fichier avec un nom tel que 8600179353689423985.tmp .

Enfin, si nous ne fournissons ni chemin, ni préfixe, ni suffixe, l'opération utilisera les valeurs par défaut partout. L'emplacement par défaut du fichier créé sera le répertoire de fichiers temporaires fourni par le système de fichiers:

@Test public void givenNoFilePath_whenCreatesTempFileInTempDir_thenCorrect() { Path p = Files.createTempFile(null, null); assertTrue(Files.exists(p)); }

Sous Windows, ce sera par défaut quelque chose comme C: \ Users \ user \ AppData \ Local \ Temp \ 6100927974988978748.tmp .

Toutes les opérations ci-dessus peuvent être adaptées pour créer des répertoires plutôt que des fichiers normaux en utilisant createTempDirectory au lieu de createTempFile .

6. Suppression d'un fichier

Pour supprimer un fichier, nous utilisons l' API de suppression . Par souci de clarté, le test suivant vérifie d'abord que le fichier n'existe pas déjà, puis le crée et confirme qu'il existe maintenant et enfin le supprime et confirme qu'il n'existe plus:

@Test public void givenPath_whenDeletes_thenCorrect() { Path p = Paths.get(HOME + "/fileToDelete.txt"); assertFalse(Files.exists(p)); Files.createFile(p); assertTrue(Files.exists(p)); Files.delete(p); assertFalse(Files.exists(p)); }

Cependant, si un fichier n'existe pas dans le système de fichiers, l'opération de suppression échouera avec une IOException :

@Test(expected = NoSuchFileException.class) public void givenInexistentFile_whenDeleteFails_thenCorrect() { Path p = Paths.get(HOME + "/inexistentFile.txt"); assertFalse(Files.exists(p)); Files.delete(p); }

Nous pouvons éviter ce scénario en utilisant deleteIfExists qui échouent silencieusement au cas où le fichier n'existe pas. Ceci est important lorsque plusieurs threads effectuent cette opération et que nous ne voulons pas d'un message d'échec simplement parce qu'un thread a effectué l'opération plus tôt que le thread actuel qui a échoué:

@Test public void givenInexistentFile_whenDeleteIfExistsWorks_thenCorrect() { Path p = Paths.get(HOME + "/inexistentFile.txt"); assertFalse(Files.exists(p)); Files.deleteIfExists(p); }

Lorsqu'il s'agit de répertoires et non de fichiers normaux, nous devons nous rappeler que l'opération de suppression ne fonctionne pas de manière récursive par défaut. Donc, si un répertoire n'est pas vide, il échouera avec une IOException :

@Test(expected = DirectoryNotEmptyException.class) public void givenPath_whenFailsToDeleteNonEmptyDir_thenCorrect() { Path dir = Paths.get( HOME + "/emptyDir" + UUID.randomUUID().toString()); Files.createDirectory(dir); assertTrue(Files.exists(dir)); Path file = dir.resolve("file.txt"); Files.createFile(file); Files.delete(dir); assertTrue(Files.exists(dir)); }

7. Copie de fichiers

Vous pouvez copier un fichier ou un répertoire à l'aide de l' API de copie :

@Test public void givenFilePath_whenCopiesToNewLocation_thenCorrect() { Path dir1 = Paths.get( HOME + "/firstdir_" + UUID.randomUUID().toString()); Path dir2 = Paths.get( HOME + "/otherdir_" + UUID.randomUUID().toString()); Files.createDirectory(dir1); Files.createDirectory(dir2); Path file1 = dir1.resolve("filetocopy.txt"); Path file2 = dir2.resolve("filetocopy.txt"); Files.createFile(file1); assertTrue(Files.exists(file1)); assertFalse(Files.exists(file2)); Files.copy(file1, file2); assertTrue(Files.exists(file2)); }

The copy fails if the target file exists unless the REPLACE_EXISTING option is specified:

@Test(expected = FileAlreadyExistsException.class) public void givenPath_whenCopyFailsDueToExistingFile_thenCorrect() { Path dir1 = Paths.get( HOME + "/firstdir_" + UUID.randomUUID().toString()); Path dir2 = Paths.get( HOME + "/otherdir_" + UUID.randomUUID().toString()); Files.createDirectory(dir1); Files.createDirectory(dir2); Path file1 = dir1.resolve("filetocopy.txt"); Path file2 = dir2.resolve("filetocopy.txt"); Files.createFile(file1); Files.createFile(file2); assertTrue(Files.exists(file1)); assertTrue(Files.exists(file2)); Files.copy(file1, file2); Files.copy(file1, file2, StandardCopyOption.REPLACE_EXISTING); }

However, when copying directories, the contents are not copied recursively. This means that if /baeldung contains /articles.db and /authors.db files, copying /baeldung to a new location will create an empty directory.

8. Moving Files

You can move a file or directory by using the move API. It is in most ways similar to the copy operation. If the copy operation is analogous to a copy and paste operation in GUI based systems, then move is analogous to a cut and paste operation:

@Test public void givenFilePath_whenMovesToNewLocation_thenCorrect() { Path dir1 = Paths.get( HOME + "/firstdir_" + UUID.randomUUID().toString()); Path dir2 = Paths.get( HOME + "/otherdir_" + UUID.randomUUID().toString()); Files.createDirectory(dir1); Files.createDirectory(dir2); Path file1 = dir1.resolve("filetocopy.txt"); Path file2 = dir2.resolve("filetocopy.txt"); Files.createFile(file1); assertTrue(Files.exists(file1)); assertFalse(Files.exists(file2)); Files.move(file1, file2); assertTrue(Files.exists(file2)); assertFalse(Files.exists(file1)); }

L' opération de déplacement échoue si le fichier cible existe, sauf si l' option REPLACE_EXISTING est spécifiée comme nous l'avons fait avec l' opération de copie :

@Test(expected = FileAlreadyExistsException.class) public void givenFilePath_whenMoveFailsDueToExistingFile_thenCorrect() { Path dir1 = Paths.get( HOME + "/firstdir_" + UUID.randomUUID().toString()); Path dir2 = Paths.get( HOME + "/otherdir_" + UUID.randomUUID().toString()); Files.createDirectory(dir1); Files.createDirectory(dir2); Path file1 = dir1.resolve("filetocopy.txt"); Path file2 = dir2.resolve("filetocopy.txt"); Files.createFile(file1); Files.createFile(file2); assertTrue(Files.exists(file1)); assertTrue(Files.exists(file2)); Files.move(file1, file2); Files.move(file1, file2, StandardCopyOption.REPLACE_EXISTING); assertTrue(Files.exists(file2)); assertFalse(Files.exists(file1)); }

9. Conclusion

Dans cet article, nous avons découvert les API de fichiers dans la nouvelle API de système de fichiers (NIO2) livrée dans le cadre de Java 7 et avons vu la plupart des opérations de fichiers importantes en action.

Les exemples de code utilisés dans cet article se trouvent dans le projet Github de l'article.