java 7 nio 文件 监视_使用Java NIO监视文件

本文介绍了如何使用Java NIO的WatchService API来监控文件系统的创建、修改和删除事件。首先,我们创建并注册一个WatchService,然后设置一个无限循环来监听并处理事件。接着,我们创建了FileEvent类和FileListener接口,用于在事件发生时通知观察者。最后,我们实现了FileWatcher类,它维护了一个监听器列表并根据事件类型调用相应的处理方法。这个高级API使得我们可以更方便地处理文件系统事件。
摘要由CSDN通过智能技术生成

通过优锐课的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 listeners = new ArrayList<>();

4

protected final File folder;

5

6

public FileWatcher(File folder) {

7

this.folder = folder;

8

}

9

10

public List getListeners() {

11

return listeners;

12

}

13

14

public FileWatcher setListeners(List 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

@Override

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

@Override

12

public void run() {

13

// implementation not yet provided

14

}

15

16

}

1

public class FileWatcher implements Runnable {

2

3

protected static final List watchServices = new ArrayList<>();

4

5

@Override

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 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 listeners = new ArrayList<>();

18

protected final File folder;

19

protected static final List 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

@Override

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 getListeners() {

90

return listeners;

91

}

92

93

public FileWatcher setListeners(List listeners) {

94

this.listeners = listeners;

95

return this;

96

}

97

98

public static List getWatchServices() {

99

return Collections.unmodifiableList(watchServices);

100

}

101

102

}

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

FileAdapter.java

public abstract class FileAdapter implements FileListener {

2

3

@Override

4

public void onCreated(FileEvent event) {

5

// no implementation provided

6

}

7

8

@Override

9

public void onModified(FileEvent event) {

10

// no implementation provided

11

}

12

13

@Override

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

@Override

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 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干货

86b28372c3f53c0b45f036faa377600e.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值