使用Java NIO监视文件

通过优锐课的java小课中学习到了很多重点,一起来看看了解有关使用Java NIO观看文件的更多信息。

该??java.nio.file??包提供了一个文件更改通知API,被称为??手表服务API。它使我们能够在监视服务中注册文件夹。注册时,我们告诉服务我们感兴趣的事件类型是:文件创建,文件修改或文件删除。

当服务检测到感兴趣的事件时,会将其转发到注册的进程并根据需要进行处理。基本上是这样的:

1.第一步是WatchService?使用类的newWatchService()?方法创建一个新??的??FileSystem。
2.接下来,我们使用Path?要关注的事件类型为要监视的文件夹注册一个实例。
3.最后,我们实现了一个无限循环来等待传入事件。当事件发生时,该密钥会发出信号,并放入观察者的队列中。处理完事件后,我们需要ready?通过调用其reset()?方法将其放回??状态??。如果返回false,则密钥不再有效,并且循环可以退出。?

4.1
5.WatchService watchService = FileSystems.getDefault().newWatchService();
6.2
7.Path path = Paths.get("c:\\directory");
8.3
9.path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
10.4
11.boolean poll = true;
12.5
13.while (poll) {
14.6
15. ?WatchKey key = watchService.take();
16.7
17. ?for (WatchEvent<?> event : key.pollEvents()) {
18.8
19. ? ?System.out.println("Event kind : " + event.kind() + " - File : " + event.context());
20.9
21.  }
22.10
23. ?poll = key.reset();
24.11
25.}

这是控制台输出:

1
Event kind : ENTRY_CREATE - File : file.txt
2
Event kind : ENTRY_DELETE - File : file.txt
3
Event kind : ENTRY_CREATE - File : test.txt
4
Event kind : ENTRY_MODIFY - File : test.txt

WatchService API的级别很低,允许我们对其进行自定义。在本文中,按照Observer模式,我们将在此机制之上设计一个高级API,以侦听给定文件夹的文件事件。我们将从创建一个FileEvent类开始,该类扩展了java.util.EventObject?所有事件状态对象的来源。甲FileEvent?实例被构造为具有到源,这是逻辑上在其上事件发生在所述文件的引用。?

FileEvent.java

import java.io.File;
2
import java.util.EventObject;
3
4
public class FileEvent extends EventObject {
5
6
 ?public FileEvent(File file) {
7
 ? ?super(file);
8
  }
9
10
 ?public File getFile() {
11
 ? ?return (File) getSource();
12
  }
13
14
}

接下来,我们创建一个FileListener必须由观察者实现的接口,以便在发生文件事件时得到通知。它扩展了java.util.EventListener接口,这是所有事件侦听器接口都必须扩展的标记接口。

FileListener.java

import java.util.EventListener;
2
3
public interface FileListener extends EventListener {
4
5
 ? ?public void onCreated(FileEvent event);
6
7
 ? ?public void onModified(FileEvent event);
8
9
 ? ?public void onDeleted(FileEvent event);
10
11
}

难题的最后一步是创建主题,该主题维护观察者列表,并通过调用其方法之一来通知状态更改。我们将其命名FileWatcher并提供一个文件夹,这就是构造此类实例的方式。

1
public class FileWatcher {
2
3
 ?protected List<FileListener> listeners = new ArrayList<>();
4
 ?protected final File folder;
5
6
 ?public FileWatcher(File folder) {
7
 ? ?this.folder = folder;
8
  }
9
10
 ?public List<FileListener> getListeners() {
11
 ? ?return listeners;
12
  }
13
14
 ?public FileWatcher setListeners(List<FileListener> listeners) {
15
 ? ?this.listeners = listeners;
16
 ? ?return this;
17
  }
18
19
}

它可以实现该Runnable接口,因此,watch()如果文件夹存在,则在调用其方法时可以使用守护程序线程启动监视进程??。

public class FileWatcher implements Runnable {
2
3
 ?public void watch() {
4
 ? ?if (folder.exists()) {
5
 ? ? ?Thread thread = new Thread(this);
6
 ? ? ?thread.setDaemon(true);
7
 ? ? ?thread.start();
8
 ?  }
9
  }
10
11
 [email protected]
12
 ?public void run() {
13
 ?// implementation not yet provided
14
  }
15
16
}

在其run()方法的实现中??,将WatchService创建一个实例以轮询try-with-resources语句中的事件。我们将使用类中的静态最终列表来对其进行跟踪FileWatcher,因此我们稍后可以调用其close()方法,以使任何等待检索键的线程抛出未选中的对象ClosedWatchServiceException,这将以干净的方式中断监视过程。因此,?当正常关闭应用程序时,我们不会收到内存泄漏警告。

public class FileWatcher implements Runnable {
2
3
 ?public void watch() {
4
 ? ?if (folder.exists()) {
5
 ? ? ?Thread thread = new Thread(this);
6
 ? ? ?thread.setDaemon(true);
7
 ? ? ?thread.start();
8
 ?  }
9
  }
10
11
 [email protected]
12
 ?public void run() {
13
 ?// implementation not yet provided
14
  }
15
16
}
1
public class FileWatcher implements Runnable {
2
3
 ?protected static final List<WatchService> watchServices = new ArrayList<>();
4
5
 [email protected]
6
 ?public void run() {
7
 ? ?try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
8
 ? ? ?Path path = Paths.get(folder.getAbsolutePath());
9
 ? ? ?path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
10
 ? ? ?watchServices.add(watchService);
11
 ? ? ?boolean poll = true;
12
 ? ? ?while (poll) {
13
 ? ? ? ?poll = pollEvents(watchService);
14
 ? ?  }
15
 ?  } catch (IOException | InterruptedException | ClosedWatchServiceException e) {
16
 ? ? ? Thread.currentThread().interrupt();
17
 ?  }
18
  }
19
20
 ?protected boolean pollEvents(WatchService watchService) throws InterruptedException {
21
 ? ?WatchKey key = watchService.take();
22
 ? ?Path path = (Path) key.watchable();
23
 ? ?for (WatchEvent<?> event : key.pollEvents()) {
24
 ? ? ?notifyListeners(event.kind(), path.resolve((Path) event.context()).toFile());
25
 ?  }
26
 ? ?return key.reset();
27
  }
28
29
 ?public static List<WatchService> getWatchServices() {
30
 ? ?return Collections.unmodifiableList(watchServices);
31
  }
32
33
}

每当发生事件时,文件路径都会被解析,并相应地通知侦听器。如果是创建新文件夹,FileWatcher则将创建另一个实例进行监视。?

public class FileWatcher implements Runnable {
2
3
 ?protected void notifyListeners(WatchEvent.Kind<?> kind, File file) {
4
 ? ?FileEvent event = new FileEvent(file);
5
 ? ?if (kind == ENTRY_CREATE) {
6
 ? ? ?for (FileListener listener : listeners) {
7
 ? ? ? ? listener.onCreated(event);
8
 ? ?  }
9
 ? ? ?if (file.isDirectory()) {
10
 ? ? ? ? // create a new FileWatcher instance to watch the new directory
11
 ? ? ? ? new FileWatcher(file).setListeners(listeners).watch();
12
 ? ?  }
13
 ?  }
14
 ? ?else if (kind == ENTRY_MODIFY) {
15
 ? ? ?for (FileListener listener : listeners) {
16
 ? ? ? ?listener.onModified(event);
17
 ? ?  }
18
 ?  }
19
 ? ?else if (kind == ENTRY_DELETE) {
20
 ? ? ?for (FileListener listener : listeners) {
21
 ? ? ? ?listener.onDeleted(event);
22
 ? ?  }
23
 ?  }
24
  }
25
26
}

这是FileWatcher该类的完整清单。

FileWatcher.java
import static java.nio.file.StandardWatchEventKinds.*;
2
import java.io.File;
3
import java.io.IOException;
4
import java.nio.file.ClosedWatchServiceException;
5
import java.nio.file.FileSystems;
6
import java.nio.file.Path;
7
import java.nio.file.Paths;
8
import java.nio.file.WatchEvent;
9
import java.nio.file.WatchKey;
10
import java.nio.file.WatchService;
11
import java.util.ArrayList;
12
import java.util.Collections;
13
import java.util.List;
14
15
public class FileWatcher implements Runnable {
16
17
 ?protected List<FileListener> listeners = new ArrayList<>();
18
 ?protected final File folder;
19
 ?protected static final List<WatchService> watchServices = new ArrayList<>();
20
 ?
21
 ?public FileWatcher(File folder) {
22
 ? ?this.folder = folder;
23
  }
24
25
 ?public void watch() {
26
 ? ?if (folder.exists()) {
27
 ? ? ?Thread thread = new Thread(this);
28
 ? ? ?thread.setDaemon(true);
29
 ? ? ?thread.start();
30
 ?  }
31
  }
32
33
 [email protected]
34
 ?public void run() {
35
 ? ?try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
36
 ? ? ?Path path = Paths.get(folder.getAbsolutePath());
37
 ? ? ?path.register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
38
 ? ? ?watchServices.add(watchService);
39
 ? ? ?boolean poll = true;
40
 ? ? ?while (poll) {
41
 ? ? ? ?poll = pollEvents(watchService);
42
 ? ?  }
43
 ?  } catch (IOException | InterruptedException | ClosedWatchServiceException e) {
44
 ? ? ? Thread.currentThread().interrupt();
45
 ?  }
46
  }
47
48
 ?protected boolean pollEvents(WatchService watchService) throws InterruptedException {
49
 ? ?WatchKey key = watchService.take();
50
 ? ?Path path = (Path) key.watchable();
51
 ? ?for (WatchEvent<?> event : key.pollEvents()) {
52
 ? ? ?notifyListeners(event.kind(), path.resolve((Path) event.context()).toFile());
53
 ?  }
54
 ? ?return key.reset();
55
  }
56
57
 ?protected void notifyListeners(WatchEvent.Kind<?> kind, File file) {
58
 ? ?FileEvent event = new FileEvent(file);
59
 ? ?if (kind == ENTRY_CREATE) {
60
 ? ? ?for (FileListener listener : listeners) {
61
 ? ? ? ?listener.onCreated(event);
62
 ? ?  }
63
 ? ? ?if (file.isDirectory()) {
64
 ? ? ? ?new FileWatcher(file).setListeners(listeners).watch();
65
 ? ?  }
66
 ? }
67
 ? else if (kind == ENTRY_MODIFY) {
68
 ? ? for (FileListener listener : listeners) {
69
 ? ? ? listener.onModified(event);
70
 ? ? }
71
 ? }
72
 ? else if (kind == ENTRY_DELETE) {
73
 ? ? for (FileListener listener : listeners) {
74
 ? ? ? listener.onDeleted(event);
75
 ? ? }
76
 ? }
77
  }
78
79
 ?public FileWatcher addListener(FileListener listener) {
80
 ? ?listeners.add(listener);
81
 ? ?return this;
82
  }
83
84
 ?public FileWatcher removeListener(FileListener listener) {
85
 ? ?listeners.remove(listener);
86
 ? ?return this;
87
  }
88
89
 ?public List<FileListener> getListeners() {
90
 ? ?return listeners;
91
  }
92
93
 ?public FileWatcher setListeners(List<FileListener> listeners) {
94
 ? ?this.listeners = listeners;
95
 ? ?return this;
96
  }
97
98
 ?public static List<WatchService> getWatchServices() {
99
 ? ?return Collections.unmodifiableList(watchServices);
100
  }
101
102
}

我们设计的最后一点就是创建一个FileAdapter类,该类提供了FileListener?接口的默认实现,因此我们只能处理很少的事件来保存代码。

FileAdapter.java
public abstract class FileAdapter implements FileListener {
2
3
 [email protected]
4
 ?public void onCreated(FileEvent event) {
5
 ? ?// no implementation provided
6
  }
7
8
 [email protected]
9
 ?public void onModified(FileEvent event) {
10
 ? // no implementation provided
11
  }
12
13
 [email protected]
14
 ?public void onDeleted(FileEvent event) {
15
 ? // no implementation provided
16
  }
17
18
}

该??FileAdapter班是在我的情况是非常有用的,发展我的IDE中的Servlet应用程序时重新加载Groovy脚本。修改文件并将其重新发布到部署目录中后,首先将其删除,然后再重新创建。因此,可以在Windows平台上触发两次的修改事件可以忽略,而在我的上下文中,删除事件是不可用的。
这是因为,当前,我们无法从Web容器中注销Servlet,过滤器或侦听器。因此,我发现没有理由在生产中启用该功能。同样,在这种使用情况下,性能也不是问题,因为很难让五个FileWatcher实例被另一个实例监视。

protected void loadScripts(File folder) {
2
 ? if (folder.exists()) {
3
    File[] files = folder.listFiles();
4
    if (files != null) {
5
     ?for (File file : files) {
6
     ? ?if (file.isFile()) {
7
        Object object = scriptManager.loadScript(file);
8
        register(object);
9
     ?  } else {
10
        loadScripts(file);
11
     ?  }
12
      }
13
 ? ? ?  }
14
 ? ? ? watch(folder);
15
 ? }
16
}
17
18
protected void watch(File folder) {
19
 ?new FileWatcher(folder).addListener(new FileAdapter() {
20
 ? [email protected]
21
 ? ?public void onCreated(FileEvent event) {
22
 ? ? ?File file = event.getFile();
23
 ? ? ?if (file.isFile()) {
24
 ? ? ? ?logger.info("processing script " + file.getName());
25
 ? ? ? ?process(file);
26
 ? ? }
27
 ? }
28
  }).watch();
29
}
30
31
protected void process(File script) {
32
 ?Object object = scriptManager.loadScript(script);
33
 ?// update the application accordingly ?
34
}

尽管据说Thread.sleep()?在单元测试中使用该方法通常不是一个好主意,但FileWatcher由于需要在操作之间进行延迟,因此我们仍将使用它为该类编写测试用例??。

import static org.junit.Assert.*;
2
import java.io.File;
3
import java.io.FileWriter;
4
import java.io.IOException;
5
import java.util.HashMap;
6
import java.util.Map;
7
import org.junit.Test;
8
9
public class FileWatcherTest {
10
11
 ? @Test
12
 ? public void test() throws IOException, InterruptedException {
13
 ? ? ?File folder = new File("src/test/resources");
14
 ? ? ?final Map<String, String> map = new HashMap<>();
15
 ? ? ?FileWatcher watcher = new FileWatcher(folder);
16
 ? ? ?watcher.addListener(new FileAdapter() {
17
 ? ? ? ? public void onCreated(FileEvent event) {
18
 ? ? ? ? ? map.put("file.created", event.getFile().getName());
19
 ? ? ? ? }
20
 ? ? ? ? public void onModified(FileEvent event) {
21
 ? ? ? ? ? map.put("file.modified", event.getFile().getName());
22
 ? ? ? ? }
23
 ? ? ? ? public void onDeleted(FileEvent event) {
24
 ? ? ? ? ? map.put("file.deleted", event.getFile().getName());
25
 ? ? ? ? }
26
 ? ?  }).watch();
27
 ? ? ?assertEquals(1, watcher.getListeners().size());
28
 ? ? ?wait(2000);
29
 ? ? ?File file = new File(folder + "/test.txt");
30
 ? ? ?try(FileWriter writer = new FileWriter(file)) {
31
 ? ? ? ?writer.write("Some String");
32
 ? ?  }
33
 ? ? ?wait(2000);
34
 ? ? ?file.delete();
35
 ? ? ?wait(2000);
36
 ? ? ?assertEquals(file.getName(), map.get("file.created"));
37
 ? ? ?assertEquals(file.getName(), map.get("file.modified"));
38
 ? ? ?assertEquals(file.getName(), map.get("file.deleted"));
39
 ? }
40
41
 ? public void wait(int time) throws InterruptedException {
42
 ? ? ?Thread.sleep(time);
43
 ? }
44
45
}
46

对于我来说,这可能是纠正其实现的绝好机会,Class.newInstance()方法是将不推荐使用的方法替换为该Class.getConstructor().newInstance()方法,以使其正确无误。

import java.io.File;
2
import java.net.URL;
3
import groovy.util.GroovyScriptEngine;
4
5
public class ScriptManager {
6
7
 ? ?protected final GroovyScriptEngine engine;
8
9
 ? ?public ScriptManager(File folder) {
10
 ? ? ?engine = createScriptEngine(folder);
11
 ?  }
12
13
 ? ?protected GroovyScriptEngine createScriptEngine(File folder) {
14
 ? ? ?URL[] urls = { folder.toURI().toURL() };
15
 ? ? ?return new GroovyScriptEngine(urls, this.getClass().getClassLoader());
16
 ?  }
17
18
 ? ?public Object loadScript(String name) {
19
 ? ? ?return engine.loadScriptByName(name).getConstructor().newInstance()
20
 ?  }
21
22
}
23

除非你在script name参数中传递相对路径,否则上述类无法加载位于给定文件夹的子目录中的脚本。这就是为什么最好这样写的原因:

import java.io.File;
2
import java.net.URL;
3
import groovy.util.GroovyScriptEngine;
4
5
public class ScriptManager {
6
 ? ?
7
 ? ?protected final File folder;
8
 ? ?protected final GroovyScriptEngine engine;
9
10
 ? ?public ScriptManager(File folder) {
11
 ? ? ?this.folder = folder;
12
 ? ? ?engine = createScriptEngine();
13
 ?  }
14
15
 ? ?protected GroovyScriptEngine createScriptEngine() {
16
 ? ? ?URL[] urls = { folder.toURI().toURL() };
17
 ? ? ?return new GroovyScriptEngine(urls, this.getClass().getClassLoader());
18
 ?  }
19
20
 ? ?public Object loadScript(File file) {
21
 ? ? ?String name = file.getAbsolutePath().substring(folder.getAbsolutePath().length() + 1);
22
 ? ? ?return engine.loadScriptByName(name).getConstructor().newInstance()
23
 ?  }
24
25
}

喜欢这篇文章的可以点个赞,欢迎大家留言评论,记得关注我,每天持续更新技术干货、职场趣事、海量面试资料等等
?> 如果你对java技术很感兴趣也可以交流学习,共同学习进步。?
不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代

文章写道这里,欢迎完善交流。最后奉上近期整理出来的一套完整的java架构思维导图,分享给大家对照知识点参考学习。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java干货

原文地址:https://blog.51cto.com/14634606/2468827

时间: 2024-07-31 20:39:52

使用Java NIO监视文件的相关文章

Java IO和Java NIO在文件拷贝上的性能差异分析 (转)

1.       在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取 到用户空间的缓冲区.参看read(byte b[])方法的源码,可知,它会在内部再调用readBytes(b, 0, b.length)方法,而且readBytes(b, 0, b.length)方法是一个native方法(即本地方法),最终通过这个本地方法来发起一次系统调用,即调用系统内核的read()

Java IO和Java NIO在文件拷贝上的性能差异分析

1.  在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区.参看read(byte b[])方法的源码,可知,它会在内部再调用readBytes(b, 0, b.length)方法,而且readBytes(b, 0, b.length)方法是一个native方法(即本地方法),最终通过这个本地方法来发起一次系统调用,即调用系统内核的read()方法,内核从

Java NIO.2 —— 文件或目录删除操作

文件删除 删除单个文件的操作很简单,如果要删除一个目录树的话,需要实现FileVisitor 接口然后递归地调用delete() 或deleteIfExists()方法.在看代码之前,需要注意一下问题. 在删除一个目录之前,要先删除里面的文件. visitFile()方式是执行删除每个文件最好的方式. 因为只有在目录为空的情况下才可以删除,所以建议在postVisitDirectory()方法中删除目录操作. 如果文件不允许访问,你可以根据自己的决定使用visitFileFailed()方法来返

Java NIO.2 —— 文件或目录拷贝操作

拷贝整个文件树是可以递归每个目录和文件调用 Files.copy()方法.在使用的时候有一下注意事项. 在往目录拷贝文件之前,首先要保证目录已经存在.拷贝源目录(不论是否为空)都会生成目标目录.整个任务一定在preVisitDirectory()方法中执行. visitFile()方式用来拷贝每个文件最佳的方法. 在你拷贝目录或文件时,你需要决定是否要使用REPLACE_EXISTING 和COPY_ATTRIBUTES 选项. 如果你想保存要拷贝的目录的属性,你需要在postVisitDire

Java NIO 读取文件、写入文件、读取写入混合

前言 Java NIO(new/inputstream outputstream)使用通道.缓冲来操作流,所以要深刻理解这些概念,尤其是,缓冲中的数据结构(当前位置(position).限制(limit).容量(capacity)),这些知识点要通过写程序慢慢体会. NIO vs  传统IO NIO是面向缓冲.通道的:传统IO面向流 通道是双向的既可以写.也可以读:传统IO只能是单向的 NIO可以设置为异步:传统IO只能是阻塞,同步的 缓冲区结构图 NIO是面向缓冲区的,缓冲区可以理解为一块内存

Java nio 的文件处理

一.创建一个大文件 下载文件时往往会创建一个指定大小的空文件 package com.lazy.nio; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file

Java NIO 实现文件复制

/* *int bytesRead=inChannel.read(buf); * 这句话是从文件流中读取一个buf内容,返回读取的大小, * 如果是读取到文件尾部的时候,返回的是-1 * * 注意FileChannel.write()是在while循环中调用的. * 因为无法保证write()方法一次能向FileChannel写入多少字节, * 因此需要重复调用write()方法, * 直到Buffer中已经没有尚未写入通道的字节. * */ @Testpublic void test1() {

java nio实现文件复制

public class TransferTo { public static void main(String[] args) throws Exception { FileChannel in = new FileInputStream("src/demo20/data.txt").getChannel(), out = new FileOutputStream("src/demo20/data2.txt").getChannel(); in.transferT

转:Java NIO(3)

要想讲清楚nio的原理和它的优点得先清楚Java应用程序的文件读写原理和虚拟内存的原理.Java文件读取原理可参见如下图: 当应用程序需要读取文件的时候,内核首先通过DMA技术将文件内容从磁盘读入内核中的buffer,然后Java应用进程再从内核的buffer将数据读取到应用程序的buffer. 为了提升I/O效率和处理能力,操作系统采用虚拟内存的机制.虚拟内存也就是我们常说的交换内存,它实际上是硬盘上的文件,虚拟内存有两个作用: 1. 不同的虚拟内存可以映射到相同的物理内存,根据这个原理,可以