Java的IO相关内容。
NIO2
文件系统访问
java.nio.file.Path类用来表示一个路径。该类有许多路径相关的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public static void main(String[] args) throws IOException { Path path = Paths.get("/Users/Mark/Project", "Base.java"); System.out.println(path); System.out.println(path.toAbsolutePath()); System.out.println(path.toRealPath()); System.out.println(path.getFileName()); System.out.println(path.getNameCount()); System.out.println(path.getRoot()); System.out.println(path.getParent()); for (int i = 0; i < path.getNameCount(); i++) { System.out.println(path.getName(i)); } path = Paths.get("src/main/java/", "com/mark/nio"); System.out.println(path); System.out.println(path.subpath(0, 3)); System.out.println(path.resolve("test")); System.out.println(path.resolveSibling("io")); Path other = Paths.get("src/main/resources"); System.out.println(other.toAbsolutePath()); path = path.relativize(other); System.out.println(path); System.out.println(path.toAbsolutePath()); System.out.println(path.toAbsolutePath().normalize()); }
|
列出目录的子目录和文件可以使用File.list和File.listFiles方法,NIO2中可以使用java.nio.file.DirectoryStream来进行遍历目录。DirectoryStream接口实现了Closeable和Iterable接口。通过Files.newDirectoryStream方法获得DirectoryStream实例,可以通过DirectoryStream.Filter来过滤。
1 2 3 4 5
| Path path = Paths.get(""); DirectoryStream<Path> ds = Files.newDirectoryStream(path, "*.java"); for (Path d : ds) { System.out.println(d); }
|
如果想递归遍历整个目录,可以使用Files.walkFileTree方法和java.nio.file.FileVisitor接口。FileVisitor接口有四个方法
- visitFile 正在访问某个文件
- visitFileFailed 访问某个文件出现错误
- preVisitDirectory 在访问一个目录中的子目录和文件之前调用
- postVisitDirectory 在访问一个目录中的子目录和文件之后调用
这四个方法都返回FileVisitResult,这是一个枚举类型,有四个值CONTINUE、TERMINATE、SKIP_SUBTREE 和 SKIP_SIBLINGS。
该接口有一个默认实现SimpleFileVisitor。我们可以继承这个类来实现我们自己的逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| Path start = Paths.get(""); Files.walkFileTree(start, new SimpleFileVisitor<Path>() { @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (file.getFileName().toString().endsWith(".java")) { System.out.println(file.toAbsolutePath()); } return FileVisitResult.CONTINUE; }
@Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { if (".git".equals(dir.getFileName().toString()) || ".idea".equals(dir.getFileName().toString())) { return FileVisitResult.SKIP_SUBTREE; } return FileVisitResult.CONTINUE; }
@Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { return super.visitFileFailed(file, exc); }
@Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { return super.postVisitDirectory(dir, exc); } });
|
文件的属性信息由java.nio.file.attribute.BasicFileAttributes、PosixFileAttributes、DosFileAttributes这几个接口定义。可以通过java.nio.file.attribute.BasicFileAttributeView、DosFileAttributeView、PosixFileAttributeView的readAttributes方法来得到。Files类也提供了一系列静态方法来获得文件的属性信息。不同类型的FileAttributes会有不同的方法。
1 2 3 4 5 6 7 8 9 10 11
| Path path = Paths.get("pom.xml"); PosixFileAttributeView fav = Files.getFileAttributeView(path, PosixFileAttributeView.class); PosixFileAttributes attributes = fav.readAttributes(); System.out.println(attributes.group()); System.out.println(attributes.owner()); System.out.println(attributes.permissions()); System.out.println(attributes.creationTime()); System.out.println(attributes.isRegularFile()); System.out.println(attributes.lastAccessTime()); System.out.println(attributes.lastModifiedTime()); System.out.println(attributes.size());
|
对一个目录进行监视可以通过java.nio.file.WatchService接口来实现,通过将要监视的目录和感兴趣的事件注册到WatchService,事件类StandardWatchEventKinds中定义了一些事件,在事件发生的时候就可以通过WatchKey来获得事件列表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Path this_dir = Paths.get("").toAbsolutePath(); System.out.println("Now watching the current directory " + this_dir.toString()); try { WatchService watcher = this_dir.getFileSystem().newWatchService(); this_dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE);
while (true) { WatchKey watchKey = watcher.take(); List<WatchEvent<?>> events = watchKey.pollEvents(); for (WatchEvent event : events) { Path context = (Path) event.context(); context = this_dir.resolve(context); System.out.println("Someone just created the file " + context.toString()); System.out.println(Files.size(context)); } watchKey.reset(); } } catch (IOException | InterruptedException e) { System.out.println("Error: " + e.toString()); }
|
zip/jar文件系统
在Java中可以通过FileSystem和FileSystemProvider这两个抽象类来实现自定义的文件系统。
Java类库中包含两种文件系统,基于操作系统的文件系统和zip/jar文件系统。
例子:将一个文件添加到zip压缩包中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public static void main(String[] args) throws IOException { File testZip = new File("归档.zip"); File fileToAdd = new File("demo.txt"); if (testZip.exists() && fileToAdd.exists()) { addFile(testZip, fileToAdd); } }
原先的方法: 先创建一个tmp文件,然后将原压缩文件中的数据写入tmp文件,再把要加入的文件数据写入tmp文件 最后删了原压缩文件,并把tmp文件改名为原压缩文件 */ private static void addFile(File zip, File file) throws IOException { File tmp = File.createTempFile(zip.getName(), null); int len = -1; try (ZipInputStream input = new ZipInputStream(new FileInputStream(zip)); ZipOutputStream output = new ZipOutputStream(new FileOutputStream(tmp))) { ZipEntry entry = input.getNextEntry(); byte[] buf = new byte[10240]; while (entry != null) { String name = entry.getName(); if (!file.getName().equals(name)) { output.putNextEntry(new ZipEntry(name)); while ((len = input.read(buf)) != -1) { output.write(buf, 0, len); } output.closeEntry(); } entry = input.getNextEntry(); } try (FileInputStream newFileInput = new FileInputStream(file)) { output.putNextEntry(new ZipEntry(file.getName())); System.out.println(newFileInput.available()); while ((len = newFileInput.read(buf)) != -1) { output.write(buf, 0, len); } output.closeEntry(); } zip.delete(); tmp.renameTo(zip); } }
private static void addFileToZip(File zip, File file) throws IOException { Map<String, String> env = new HashMap<>(); env.put("create", "true"); try (FileSystem fs = FileSystems.newFileSystem(URI.create("jar:" + zip.toURI()), env)) { Path path = file.toPath(); Path pathInZip = fs.getPath("/test/" + file.getName()); Files.copy(path, pathInZip, StandardCopyOption.REPLACE_EXISTING); } }
|
异步I/O通道
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| private static void server() throws IOException { AsynchronousChannelGroup group = AsynchronousChannelGroup.withFixedThreadPool(10, Executors.defaultThreadFactory()); AsynchronousServerSocketChannel channel = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress(8680)); channel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { @Override public void completed(AsynchronousSocketChannel result, Void attachment) { channel.accept(null, this); try { System.out.println(result.getRemoteAddress()); result.write(ByteBuffer.wrap("hi there!".getBytes(StandardCharsets.UTF_8))); result.close(); } catch (IOException e) { e.printStackTrace(); } }
@Override public void failed(Throwable exc, Void attachment) { throw new RuntimeException(exc); }
}); }
private static void writeToFile() throws IOException, InterruptedException, ExecutionException { AsynchronousFileChannel afc = AsynchronousFileChannel.open(Paths.get("demo.txt"), StandardOpenOption.CREATE, StandardOpenOption.WRITE); Future<Integer> future = afc.write(ByteBuffer.wrap("hello world".getBytes()), 0); System.out.println(future.get()); }
|
AsynchronousServerSocketChannel实现了NetworkChannel接口,该接口是是与网络套接字相关的通道,提供了绑定和配置相关的方法。