WatchService监控目录和文件变化

两种监控文件和目录方式

Commons-io

  • 在Apache的Commons-io提供文件的监控功能。
  • 由文件监控类FileAlterationMonitor中的线程不停的扫描文件观察器FileAlterationObserver。
  • 如果有文件的变化,则根据相关的文件比较器,判断文件时新增,还是删除,还是更改。(默认为1000毫秒执行一次扫描)

WatchService

  • Java1.7中提供了NIO WatchService来监控系统中文件的变化。
  • 该监控是基于操作系统的文件系统监控器,可以监控系统是所有文件的变化,这种监控是无需遍历、无需比较的,是一种基于信号收发的监控。

整个监控目录文件操作的流程大致如下:

  • 获取 WatchService
  • 注册指定目录的监视器
  • 等待目录下的文件发生变化
  • 对发生变化的文件进行操作

    WatchService文件监控

    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
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    @Slf4j
    public class FileWatchTask implements Runnable {
    private String fileDirectory;
    private List<String> monitorFileNameList = new ArrayList<>();
    private ParseIF parseIF;
    /**
    * @param fileDirectory 监控目录
    * @param monitorFileNameList 监控文件名
    * @param parseIF 处理函数
    */
    public FileWatchTask(String fileDirectory, List<String> monitorFileNameList, ParseIF parseIF) {
    this.fileDirectory = fileDirectory;
    if (monitorFileNameList != null) {
    this.monitorFileNameList = monitorFileNameList;
    }
    this.parseIF = parseIF;
    }

    @Override
    public void run() {
    WatchService service = null;
    try {
    //获取当前文件系统的监控对象
    service = FileSystems.getDefault().newWatchService();
    } catch (Exception e) {
    e.printStackTrace();
    }
    try {
    //获取文件目录下的Path对象注册到 watchService中。
    //监听的事件类型,有创建,删除,以及修改
    Paths.get(fileDirectory)
    .register(service, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
    StandardWatchEventKinds.ENTRY_MODIFY);
    } catch (IOException e) {
    e.printStackTrace();
    }
    while (true) {
    //WatchKey 类代表了这个监听服务的注册,可以用它来获取事件的各个属性。
    WatchKey key = null;
    try {
    //获取可用key.没有可用的就wait
    key = service.take();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    for (WatchEvent<?> event : key.pollEvents()) {
    log.warn(event.context() + "-" + event.kind() + "-" + event.count());

    final WatchEvent.Kind<?> kind = event.kind();
    if (kind == StandardWatchEventKinds.OVERFLOW) {
    continue;
    }

    // get the filename for the event
    final WatchEvent<Path> watchEventPath = (WatchEvent<Path>) event;
    final Path fileName = watchEventPath.context();
    if (monitorFileNameList.stream().noneMatch(e -> e.equals(fileName.getFileName().toString()))) {
    continue;
    }

    //创建事件
    if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
    parseIF.parse(FileStatus.CREATE);
    }
    //修改事件
    if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
    parseIF.parse(FileStatus.MODIFY);
    }
    //删除事件
    if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
    parseIF.parse(FileStatus.DELETE);
    }
    }
    //重置,这一步很重要,否则当前的key就不再会获取将来发生的事件
    boolean valid = key.reset();
    //失效状态,退出监听
    if (!valid) {
    break;
    }
    }

    }
    }

文件类型

1
2
3
public enum FileStatus {
CREATE, DELETE, MODIFY
}

处理函数

@FunctionalInterface
public interface ParseIF {
boolean parse(FileStatus type);
}

线程池

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
/**
* 目录监控
*
* @return
*/
private synchronized boolean monitor() {
if (monitor) {
return false;
}

monitor = true;

//因为是线程安全的所以可以放入ThreadPool中使用
ExecutorService cachedThreadPool = newFixedThreadPool(1);

//监控文件
List<String> monitorFileNameList = Arrays.asList("a.txt", "b.txt");

//处理
ParseIF parseIF = (type) -> {
if (type == FileStatus.CREATE || type == FileStatus.MODIFY) {
reload(filePath);
} else {
return false;
}
return true;
};

//监控目录、文件及处理
cachedThreadPool.execute(new FileWatchTask(filePath+"/c", monitorFileNameList, parseIF));

return true;
}
------ 本文结束------

本文标题:WatchService监控目录和文件变化

文章作者:Perkins

发布时间:2019年08月21日

原始链接:https://perkins4j2.github.io/posts/34381/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。