Post on 07-Jul-2015
description
1
Abstract
• Revue rapide des I/O avec Java
• Vue d’ensemble de l’API NIO2
• Fournie avec Java SE 7
• Pour les développeurs que nous sommes
2
Speaker
http://www.jmdoudoux.fr
@jmdoudoux
Auteur de 2 didacticiels
sous licence GNU FDL
Développons en Java (2400 pages)
Développons en Java avec Eclipse (600 pages)
Membre du et du CA du
3
Agenda
• Les entrées / sorties avec Java
• NIO 2
• L’API Filesystem
• Lectures / écritures : API mises à jour
• Les E/S asynchrones (Asynchronous I/O)
4
5
Les entrées / sorties avec Java
L’API java.io
• Depuis Java 1.0
• Flux : octets (Input/OutputStream)
caractères (Reader/Writer)
• Fichiers, In Memory, Pipes, Sérialisation, …
• Filtre, Buffer, … (adaptateur, décorateur)
• La classe File
6
7
Java 1.1 getCanonicalPath()
Java 1.2 getParentFile(), getAbsoluteFile(), getCanonicalFile(),
toURL(), isHidden(), createNewFile(), deleteOnExit(),
listFiles(), setLastModified(), setReadOnly(), listRoots(),
createTempFile(), compareTo()
Java 1.4 création à partir d’une URI, toURI()
Java 6 setWritable(), setReadable(), setExecutable(), canExecute(),
getTotalSpace(), getFreeSpace(), getUsableSpace()
Les évolutions de la classe File
Les défauts de java.io
• Le manque d’opérations basiques (copie, déplacement de
fichiers, …)
• Ne fonctionne pas de la même manière sur toutes les
plateformes
• Support limité des liens symboliques et des méta-données
• …
8
Les défauts de la classe File
• Encapsule le chemin et les opérations
• Gestion de certaines erreurs : certaines méthodes renvoient
un booléen sans lever d’exceptions (delete() par exemple)
• Manque de performance de certaines méthodes
• …
9
New IO (NIO)
• Java 1.4
• utilisation de blocs et non de flux
• les canaux et sélecteurs (channels et selectors),
• les tampons (buffers),
• encodage et décodage de caractères (charset)
10
11
NIO 2
NIO 2
Inclus dans Java SE 7
JSR 203 : ajout de fonctionnalités à l’API NIO
• Accès et manipulation du système de fichiers
• Mise à jour de l’API existante
• Canaux asynchrones (asynchronous channels)
• …
12
L’API FileSystem
• API moderne et complète
• Accès et gestion des systèmes de fichiers (fichiers,
répertoires, liens symboliques, …)
• Support des méta-datas
• Parcours des répertoires, notifications
• Extensible
13
L’API FileSystem
14
15
Les chemins
L’interface Path
• Immuable, encapsule tout ou partie du chemin d’un élément
du système de fichiers
• Dépendant du système de fichier
• Chemin encapsulé absolu ou relatif existe ou non
• Pas de gestion des extensions des fichiers
16
Obtenir une instance de type Path
17
Paths.get("monfichier.txt"); Paths.get("jm/AppData/Local/Temp/monfichier.txt");
Paths.get("C:/Users/jm/AppData/Local/Temp/monfichier.txt");
Paths.get("C:\\Users\\jm\\AppData\\Local\\Temp\\monfichier.txt");
Paths.get(URI
.create("file:///C:/Users/jm/AppData/Local/Temp/monfichier.txt"));
Paths.get(System.getProperty("java.io.tmpdir"), "monfichier.txt");
FileSystems.getDefault().getPath("logs", "access.log");
// Sur Unix
// Path path = Paths.get("/home/jm/temp/monfichier.txt");
Path : gérer les éléments
Méthodes pour gérer les éléments hiérarchiques du chemin
18
Path getFileName() nom du dernier élément
Path getParent() chemin parent ou null
Path getRoot() racine du chemin ou null
Path subPath(int, int) sous chemin
Path getName(int) élément à l’index fourni
int getNameCount() nombre d’éléments dans le chemin
Path : gérer les éléments
19
toString() = C:\Users\jm\AppData\Local\Temp\monfichier.txt
getFileName() = monfichier.txt
getRoot() = C:\
getName(0) = Users
getNameCount() = 6
getParent() = C:\Users\jm\AppData\Local\Temp
subpath(0,3) = Users\jm\AppData
toString() = jm\AppData\Local\Temp\monfichier.txt
getFileName() = monfichier.txt
getRoot() = null
getName(0) = jm
getNameCount() = 5
getParent() = jm\AppData\Local\Temp
subpath(0,3) = jm\AppData\Local
Path : manipuler le chemin
• Path normalize() :
supprime les éléments redondants (par exemple . et ..),
purement syntaxique
• Path resolve(Path) :
combiner deux chemins
• Path relativize(Path) :
chemin relatif entre le chemin et celui fourni
20
Manipuler un Path
21
path = Paths.get("C:/Users/admin/
./../jm/AppData/Local/Temp/./monfichier.txt");
System.out.println("normalize() = " + path.normalize());
normalize() = C:\Users\jm\AppData\Local\Temp\monfichier.txt
Path path = Paths.get("C:/Users/jm/AppData/Local/");
Path nouveauPath = path.resolve("Temp/monfichier.txt");
System.out.println(nouveauPath);
nouveauPath = path.resolve("C:/Temp");
System.out.println(nouveauPath);
C:\Users\jm\AppData\Local\Temp\monfichier.txt
C:\Temp
Path : comparer des chemins
Plusieurs méthodes pour comparer les chemins
• int compareTo(Path other)
• boolean endsWith(Path other)
boolean endsWith(String other)
• boolean startsWith(Path other)
boolean startsWith(String other)
• …
22
Path : convertir des chemins
Plusieurs méthodes pour convertir les chemins
• Path toAbsolutePath() :
retourner le chemin absolu du chemin
• Path toRealPath(LinkOption…) :
retourner le chemin physique avec résolution des liens
symboliques selon les options fournies
23
Path : intégration dans l’existant
• Path File.toPath()
• File Path.toFile()
• URI Path.toUri() : retourner le chemin sous la forme d’une
URI
24
25
La manipulation des fichiers
et des répertoires
Glob
• Pattern à appliquer sur un élément du système de fichiers
• Sous ensemble des expressions régulières :
* ** ? [] {} \
Exemples :
26
*.java éléments dont le nom fini par .java
??? éléments dont le nom est composé de trois
alphanumériques
A*.java éléments dont le nom commence par un a et se
termine par .java
*[0-9]* éléments dont le nom contient au moins un
chiffre
*.{htm,html} éléments dont le nom se termine par htm ou html
Glob
27
final Path file1 = Paths.get("C:/java/test/test.java");
final Path file2 = Paths.get("C:/java/test/test.txt");
final Path file3 = file1.getFileName();
String pattern = "glob:**/*.{java,class}";
System.out.println("Pattern " + pattern);
PathMatcher matcher = FileSystems.getDefault().getPathMatcher(pattern);
System.out.println(file1 + " " + matcher.matches(file1));
System.out.format("%-22s %b\n", file2, matcher.matches(file2));
System.out.format("%-22s %b\n", file3, matcher.matches(file3));
System.out.println("");
pattern = "glob:*.java";
System.out.println("Pattern " + pattern);
matcher = FileSystems.getDefault().getPathMatcher(pattern);
System.out.println(file1 + " " + matcher.matches(file1));
System.out.format("%-22s %b\n", file3, matcher.matches(file3));
La classe Files
• Création : createDirectory(), createFile(), createLink(),
createSymbolicLink(), createTempFile(), createTempDirectory(), …
• Manipulation : delete(), move(), copy(), …
• Type d’un élément : isRegularFile(), isDirectory(),
probeContentType(), …
• Méta-données et permissions : getAttributes(),
getPosixFilePermissions(), isReadable(), isWriteable(), size(),
getFileAttributeView(), …
28
Copier un fichier
29
Path source = Paths.get("c:/java/fichier.txt");
Path cible = Paths.get("c:/java/fichier_copie.txt");
try {
Files.copy(source, cible, REPLACE_EXISTING,
COPY_ATTRIBUTES);
// java.lang.UnsupportedOperationException
// Files.move(source, cible, REPLACE_EXISTING,
// COPY_ATTRIBUTES, ATOMIC_MOVE);
} catch(IOException ioe) {
// traitement en cas d’erreur
}
Copie d’un répertoire
Déplacer ou renommer un fichier
30
Path source = Paths.get("c:/java/fichier.txt");
Path cible = Paths.get("c:/java/fichier_copie.txt");
try {
Files.copy(source, cible, REPLACE_EXISTING,
COPY_ATTRIBUTES);
// java.lang.UnsupportedOperationException
// Files.move(source, cible, REPLACE_EXISTING,
// COPY_ATTRIBUTES, ATOMIC_MOVE);
} catch(IOException ioe) {
// traitement en cas d’erreur
}
Déplacement d’un répertoire
Supprimer un fichier
31
Path path = Paths.get("C:/java/test/monfichier_copie.txt");
try {
Files.delete(path);
} catch (NoSuchFileException nsfee) {
// traitement en cas d’erreur
} catch (DirectoryNotEmptyException dnee) {
// traitement en cas d’erreur
} catch (IOException ioe) {
// traitement en cas d’erreur
}
Path path = Paths.get("C:/java/test/monfichier_copie.txt");
try {
Files.deleteIfExists(path);
} catch (DirectoryNotEmptyException dnee) {
// traitement en cas d’erreur
} catch (IOException ioe) {
// traitement en cas d’erreur
}
Suppression d’un répertoire vide
Les liens symboliques
• Support optionnel selon le FS (Unix)
• Suivis par défaut avec quelques exceptions :
delete(), move(), walkFileTree()
• Files.isSameFile()
• Files.isSymbolicLink()
• Files.readSymbolicLink()
32
Créer un lien / un lien symbolique
33
Path lien = Paths.get("C:/java/test/monlien");
Path cible = Paths.get("C:/java/test/monfichier.txt");
Files.createLink(lien, cible);
if (Files.isSameFile(lien, cible)) {
System.out.println("Identique");
} else {
System.out.println("Non identique");
}
Path lien = Paths.get("/home/jm/monlien");
Path cible = Paths.get("/home/jm/monfichier.txt");
Files.createSymbolicLink(lien, cible);
if (Files.isSameFile(lien, cible)) {
System.out.println("Identique");
} else {
System.out.println("Non identique");
}
34
Les événements sur un répertoire
L’interface WatchService
• Notifications de changements dans un répertoire
• Abonnement à des événements lors de la création,
modification, suppression de fichiers
• Evite d’avoir à écrire du code de type pooling ou une API
open source (JNotify ou JPathWatch)
• Non récursif, performant
35
L’utilisation de WatchService
Mise en œuvre particulière :
• Instanciation avec FileSystem.newWatchService()
• Enregistrement avec Path.register()
(Path implémente Watchable)
• Obtenir et traiter les événements
36
WatchService
37
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("c:/java/test");
WatchKey key = dir.register(watcher, ENTRY_CREATE)
for (;;) {
try {
key = watcher.take();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
Path name = (Path) event.context();
System.out.format(event.kind() + " " + "%s created", name);
}
}
key.reset();
}
38
Les méta-données
Les attributs
• Gestion complète des attributs qui sont dépendants du
système sous jacent
• Taille, type d’éléments, caché, …
• Attributs communs (BasicFileAttributes) et spécifiques
(DosFileAttributes, PosixFileAttributes)
• Méthodes de la classe Files
39
Les attributs DOS
40
Path fichier = Paths.get("c:/java/test/test.txt");
FileTime now = FileTime.fromMillis(System.currentTimeMillis());
Files.setLastModifiedTime(fichier, now);
Files.setAttribute(fichier, "dos:hidden", true);
DosFileAttributes attr = Files.readAttributes(fichier, DosFileAttributes.class);
System.out.println(attr.isReadOnly());
System.out.println(attr.isHidden());
System.out.println(attr.isRegularFile());
System.out.println(attr.isSystem());
System.out.println(attr.lastModifiedTime());
Les attributs Posix
• Énumération PosixFilePermission
• PosixFilePermissions : helper
• FileAttribute : encapsule les attributs
41
Attention aux restrictions de droits via umask
ou via le répertoire parent
Les attributs Posix
42
Path fichier = Paths.get("/home/jm/test.txt");
PosixFileAttributes attrs = Files.readAttributes(fichier, PosixFileAttributes.class);
UserPrincipal owner = attrs.owner();
GroupPrincipal group = attrs.group();
System.out.println(owner);
System.out.println(group);
Path fichier = Paths.get("/home/jm/test.txt");
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-rw-rw-");
FileAttribute<Set<PosixFilePermission>> attr =
PosixFilePermissions.asFileAttribute(perms);
Files.createFile(fichier, attr);
Les attributs avec les vues
• Les vues permettent d’obtenir les attributs d’un même
groupe en bloc -> performance
• Les vues sont spécialisées
• Readonly ou mutable
• Création possible de vues personnalisées
43
Les différentes vues
AttributeView
FileAttributeView
BasicFileAttributeView
DosFileAttributeView
PosixFileAttributeView
FileOwnerAttributeView
AclFileAttributeView
PosixFileAttributeView
UserDefinedFileAttributeView
FileStoreAttributeView
44
BasicFileAttributView
45
Path fichier = Paths.get("c:/java/test.txt");
BasicFileAttributeView view = Files.getFileAttributeView(fichier,
BasicFileAttributeView.class);
BasicFileAttributes attributes = view.readAttributes();
FileTime creationTime = attributes.creationTime();
FileTime lastAccessTime = attributes.lastAccessTime();
boolean isRegularFile = attributes.isRegularFile();
La classe FileStore
• Encapsule un système de stockage : un disque dur, une
partition, …
• Obtenir des informations
• Connaître les AttributViews supportées
• FileStore Files.getFileStore(Path)
46
FileStore
47
for (final FileStore store : FileSystems.getDefault().getFileStores()) {
System.out.println(store);
System.out.println("nom : " + store.name() + ", type : " + fileStore.type());
System.out.println("Support BasicFileAttribute : "
+ store.supportsFileAttributeView(BasicFileAttributeView.class));
System.out.println("Support DosFileAttribute : "
+ store.supportsFileAttributeView(DosFileAttributeView.class));
System.out.println("Support PosixFileAttribute : "
+ store.supportsFileAttributeView(PosixFileAttributeView.class));
}
final int UN_GIGA = 1024 * 1024 * 1024;
for (final FileStore store : FileSystems.getDefault().getFileStores()) {
try {
final long total = store.getTotalSpace() / UN_GIGA;
final long used = (store.getTotalSpace() - store.getUnallocatedSpace())
/ UN_GIGA;
final long avail = store.getUsableSpace() / UN_GIGA;
System.out.format("%-20s total=%5dGo used=%5dGo avail=%5dGo%n",
store, total, used, avail);
} catch (final IOException e) {
e.printStackTrace();
}
}
La classe FileSystem
• Encapsule un système de fichiers
• Fabrique pour des objets de l’API
• Obtenir l’instance du système par défaut
FileSystem fs = FilesSystems.getDefault();
48
La classe FileSystem
• Extensible
• Peut permettre d’offrir différentes vues d’un système de
fichiers
(cacher des fichiers sensibles, accès en lecture seule, …)
• N’a pas besoin d’être lié à un « vrai » système de fichiers
49
Les providers de FileSystem
• Invoquer une fabrique (FileSystems) pour obtenir une
implémentation spécifique
• Utiliser ou de créer des fournisseurs de FileSystem
class MonFileSystem extends FileSystem;
• java.nio.file.spi.FileSystemProvider
50
Un provider pour les zip
• Permet de traiter le contenu d’un zip comme un système de
fichiers
• Facilite l’utilisation des archives de type zip
• Fourni en standard
51
Afficher un fichier d’un zip
52
Path jarfile = Paths.get("c:/java/archive.jar");
FileSystem fs = FileSystems.newFileSystem(jarfile, null);
Path mf = fs.getPath("META-INF", "MANIFEST.MF");
try (BufferedReader readBuffer =
Files.newBufferedReader(mf, Charset.defaultCharset())) {
String ligne = "";
while ((ligne = readBuffer.readLine()) != null) {
System.out.println(ligne);
}
}
Extraire et ajouter un fichier à un zip
53
Path jarfile = Paths.get("c:/java/archive.jar");
FileSystem fs = FileSystems.newFileSystem(jarfile, null);
Path cible = Paths.get("c:/java/MANIFEST.MF");
Files.deleteIfExists(cible);
// extaire un élément de l'archive
Files.copy(fs.getPath("/META-INF/MANIFEST.MF"), cible);
54
Parcourir le contenu d’un répertoire
DirectoryStream et WalkFileTree
L’interface DirectoryStream
• Itération sur le contenu d’un répertoire
• Performance sur de gros répertoires, consomme moins de
ressources
• Possibilité d’appliquer un filtre avec un glob
• Invoquer la méthode close() après utilisation
55
DirectoryStream
56
Path dir = Paths.get("C:/java/projets");
try (DirectoryStream<Path> stream =
Files.newDirectoryStream(dir, "*.java")) {
for (Path entry : stream) {
System.out.println(entry.getFileName());
}
}
WalkFileTree avec FileVisitor
• Parcourir une arborescence en utilisant le design pattern
Visiteur (opérations récursives)
• Files.walkFileTree()
• FileVisitor invoqué
sur chaque fichier (visitFile() / visitFileFailed())
sur chaque répertoire (preVisitDirectory()/postVisitDirectory())
57
WalkFileTree avec FileVisitor
• SimpleFileVisitor : contrôle du parcours par la valeur de
retour (CONTINUE, SKIP_SUBTREE, TERMINATE, …)
• Liens symboliques non suivis par défaut
(FileVisitOption.FOLLOW_LINKS)
• détection des références circulaires (méthode
visitFileFailed() avec FileSystemLoopException)
58
WalkFileTree
59
Path dir = Paths.get("C:/java/projets");
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
String nom = file.getFileName().toString();
if (nom.endsWith(".java")) {
System.out.println("Fichier : " + nom);
}
return FileVisitResult.CONTINUE;
}
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
System.out.println("Repertoire : " + dir);
return FileVisitResult.CONTINUE;
}
});
60
Lectures / écritures
Mises à jour de l’API
Gestion des IO avec Java
61
IO
Java 1.0
Synchrone
Bloquant
File InputStream OutputStream Reader Writer Socket RandomAccessFile
NIO
Java 1.4 (JSR 51)
Synchrone
Non bloquant
FileChannel SocketChannel ServerSocketChannel (Charset, Selector, ByteBuffer)
NIO 2
Java 7 (JSR 203)
ASynchrone
Non bloquant
Path AsynchronousFileChannel AsynchronousByteChannel AsynchronousSocketChannel AsynchronousServerSocketChannel SeekableByteChannel
Les méthodes de la classe Files
62
Besoins
avancés
Besoins
simples
Lire et écrire tout le contenu d’un fichier
63
byte[] bytes = Files.readAllBytes(path);
List<String> lignes = Files.readAllLines(path,
Charset.defaultCharset());
Files.write(path, bytes);
Files.write(path, lignes, Charset.defaultCharset());
Ecrire dans un fichier texte
64
final Path fichier =
Paths.get("c:/java/test/donnees.txt");
final Charset charset = Charset.forName("UTF8");
final String contenu = "Prix 10€";
Files.deleteIfExists(fichier);
try (BufferedWriter writer =
Files.newBufferedWriter(fichier, charset)) {
writer.write(contenu, 0, contenu.length());
} catch (IOException ioe) {
// traitement de l'erreur
}
}
L’interface SeekableByteChannel
• ByteChannel qui gère une position
• Channel équivalent à RandomAccessFile
• Files.newSeekableByteChannel()
• CREATE, CREATE_NEW, READ, WRITE, APPEND,
TRUNCATE_EXISTING, NOFOLLOW_LINKS, SYNC,
DSYNC, …
65
SeekableByteChannel
66
ByteBuffer donneesBonjour =
ByteBuffer.wrap("Bonjour".getBytes());
ByteBuffer donneesBonsoir =
ByteBuffer.wrap("Bonsoir".getBytes());
Path path = Paths.get("C:/java/test/fichier.bin");
Files.deleteIfExists(path);
try (FileChannel fileChannel = FileChannel.open(path,
StandardOpenOption.CREATE, StandardOpenOption.WRITE,
StandardOpenOption.SYNC)) {
fileChannel.position(100);
fileChannel.write(donneesBonjour);
}
try (SeekableByteChannel sbc = Files.newByteChannel(path,
StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
sbc.position(200);
sbc.write(donneesBonsoir);
}
67
Lectures / écritures
asynchrones
Les canaux asynchrones
• Channel : opérations non bloquantes (lecture, écriture)
• AsynchronousChannel : opérations asynchrones (exécutées
dans un thread)
• Contrôle des opérations après leur initialisation via deux
solutions :
java.util.concurrent.Future
java.nio.channels.CompletionHandler
68
Canaux async avec CompletionHandler
• Callback qui sera invoqué lorsque l’opération se termine
(bien ou mal)
• Paramétré avec le type de résultat et un objet en
attachement (contexte qui peut être null)
69
interface CompletionHandler<V, A> {
void completed(V result, A attachment);
void failed(Throwable t, A attachment);
}
Canaux async : les groupes
• Les CompletionHandler sont invoqués par les threads
• AsynchronousChannelGroup encapsule un pool de threads
• FixedThreadPool
• CachedThreadPool
70
71
Conclusion
Conclusion
• NIO2 apporte de nombreuses fonctionnalités, attendues
depuis longtemps
• Implémentation de bas niveau de certaines, mais utilisables
dans différents contextes
• Pour allez plus loin : Javadoc, Java tutorials (Basic I/O),
exemples du JDK (sample/nio/file)
72
73