From acd226fe1005aaefdaa021c7690bab120bc67431 Mon Sep 17 00:00:00 2001 From: Abel-Huang <497720975@qq.com> Date: Tue, 11 Jun 2019 00:35:28 +0800 Subject: [PATCH] add cp 9.4 --- docs/book/17-Files.md | 126 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/docs/book/17-Files.md b/docs/book/17-Files.md index 08a09fc..4c6feb1 100644 --- a/docs/book/17-Files.md +++ b/docs/book/17-Files.md @@ -509,6 +509,132 @@ File Attribute Views: [owner, dos, acl, basic, user] ## 路径监听 +通过**WatchService** 可以设置一个进程对目录中的更改做出响应。在这个例子中,**delTxtFiles()** 作为一个单独的任务执行,该任务将遍历整个目录并删除以 **.txt** 结尾的所有文件,**WatchService** 会对文件删除操作做出反应: + +```java +// files/PathWatcher.java +// {ExcludeFromGradle} +import java.io.IOException; +import java.nio.file.*; +import static java.nio.file.StandardWatchEventKinds.*; +import java.util.concurrent.*; + +public class PathWatcher { + static Path test = Paths.get("test"); + + static void delTxtFiles() { + try { + Files.walk(test) + .filter(f -> + f.toString() + .endsWith(".txt")) + .forEach(f -> { + try { + System.out.println("deleting " + f); + Files.delete(f); + } catch(IOException e) { + throw new RuntimeException(e); + } + }); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws Exception { + Directories.refreshTestDir(); + Directories.populateTestDir(); + Files.createFile(test.resolve("Hello.txt")); + WatchService watcher = FileSystems.getDefault().newWatchService(); + test.register(watcher, ENTRY_DELETE); + Executors.newSingleThreadScheduledExecutor() + .schedule(PathWatcher::delTxtFiles, + 250, TimeUnit.MILLISECONDS); + WatchKey key = watcher.take(); + for(WatchEvent evt : key.pollEvents()) { + System.out.println("evt.context(): " + evt.context() + + "\nevt.count(): " + evt.count() + + "\nevt.kind(): " + evt.kind()); + System.exit(0); + } + } +} +/* Output: +deleting test\bag\foo\bar\baz\File.txt +deleting test\bar\baz\bag\foo\File.txt +deleting test\baz\bag\foo\bar\File.txt +deleting test\foo\bar\baz\bag\File.txt +deleting test\Hello.txt +evt.context(): Hello.txt +evt.count(): 1 +evt.kind(): ENTRY_DELETE +*/ +``` + +**delTxtFiles()** 中的**try** 代码块看起来有些多余,因为它们捕获的是同一种类型的异常,外部的**try** 语句似乎已经足够了。然而出于某种原因,Java要求两者都必须存在(这也可能是一个bug)。还要注意的是在**filter()** 中,我们必须显式地使用**f.toString()** 转为字符串,否则我们调用**endsWith()** 将会与整个**Path**对象进行比较,而不是路径名称字符串的一部分进行比较。 + +一旦我们从**FileSystem** 中得到了**WatchService** 对象,我们将其注册到**tets** 路径以及我们感兴趣的项目的变量参数列表中,可以选择**ENTRY_CREATE**,**ENTRY_DELETE** 或**ENTRY_MODIFY**(其中创建和删除不属于修改)。 + +因为接下来对**watcher.take()** 的调用会在发生某些事情之前停止所有操作,所以我们希望**deltxtfiles()** 能够并行运行以便生成我们感兴趣的事件。为了实现这个目的,我通过调用**Executors.newSingleThreadScheduledExecutor()**产生一个**ScheduledExecutorService** 对象,然后调用**schedule()** 方法传递所需函数的方法引用,设置以及在运行之前应该等待的时间。 + +此时,**watcher.take()** 将等待并阻塞在这里。当目标事件发生时,会返回一个包含**WatchEvent**的**Watchkey**对象。展示的这三种方法是对**WatchEvent** 执行的全部操作。 + +查看输出的具体内容。即使我们正在删除以 **.txt** 结尾的文件,在**hello.txt** 被删除之前,**WatchService** 也不会被触发。你可能认为,如果说"监视这个目录",自然会包含整个目录和下面子目录,但实际上的:只会监视给定的目录,而不是下面的所有内容。如果需要监视整个树目录,必须在整个树的每个子目录上放置一个**Watchservice**。 + +```java +// files/TreeWatcher.java +// {ExcludeFromGradle} +import java.io.IOException; +import java.nio.file.*; +import static java.nio.file.StandardWatchEventKinds.*; +import java.util.concurrent.*; + +public class TreeWatcher { + + static void watchDir(Path dir) { + try { + WatchService watcher = + FileSystems.getDefault().newWatchService(); + dir.register(watcher, ENTRY_DELETE); + Executors.newSingleThreadExecutor().submit(() -> { + try { + WatchKey key = watcher.take(); + for(WatchEvent evt : key.pollEvents()) { + System.out.println( + "evt.context(): " + evt.context() + + "\nevt.count(): " + evt.count() + + "\nevt.kind(): " + evt.kind()); + System.exit(0); + } + } catch(InterruptedException e) { + return; + } + }); + } catch(IOException e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) throws Exception { + Directories.refreshTestDir(); + Directories.populateTestDir(); + Files.walk(Paths.get("test")) + .filter(Files::isDirectory) + .forEach(TreeWatcher::watchDir); + PathWatcher.delTxtFiles(); + } +} + +/* Output: +deleting test\bag\foo\bar\baz\File.txt +deleting test\bar\baz\bag\foo\File.txt +evt.context(): File.txt +evt.count(): 1 +evt.kind(): ENTRY_DELETE +*/ +``` + +在**watchDir()** 方法中给**WatchSevice**提供参数**ENTRY_DELETE**,并启动一个独立的线程来监视该**atchserviceW**。这里我们没有使用**schedule()** 进行启动,而是使用**submit()** 启动线程。我们遍历整个目录树,并将**watchDir()** 应用于每个子目录。现在,当我们运行**deltxtfiles()** 时,其中一个**Watchservice**s 会检测到每一次文件删除。 ## 文件查找