All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.godmao.io.monitor.FileMonitor Maven / Gradle / Ivy

The newest version!
package cn.godmao.io.monitor;

import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 文件监听器
 *
 * 

* 监听器可监听目录或文件
* 如果监听的Path不存在,则递归创建空目录然后监听此空目录
* 递归监听目录时,并不会监听新创建的目录 */ public class FileMonitor extends Thread implements Closeable { /** * 监听路径,必须为目录 */ private Path path; /** * 监听的文件,对于单文件监听不为空 */ private Path filePath; /** * 监听服务 */ private WatchService watchService; /** * 监听是否已经开启 */ private boolean running = false; /** * 监听器 */ private final List listeners; /** * WatchKey 和 Path的对应表 */ private final Map watchKeyPathMap = new HashMap<>(); //------------------------------------------------------ Static method start /** * 创建并初始化监听,监听所有事件 * * @param uri URI * @param listeners {@link FileListener} * @return FileMonitor */ public static FileMonitor create(URI uri, FileListener... listeners) throws IOException { return create(Paths.get(uri), listeners); } /** * 创建并初始化监听,监听所有事件 * * @param url URL * @param listeners {@link FileListener} * @return FileMonitor */ public static FileMonitor create(URL url, FileListener... listeners) throws URISyntaxException, IOException { return create(url.toURI(), listeners); } /** * 创建并初始化监听,监听所有事件 * * @param file 被监听文件 * @param listeners {@link FileListener} * @return FileMonitor */ public static FileMonitor create(File file, FileListener... listeners) throws IOException { return create(file.toPath(), listeners); } /** * 创建并初始化监听,监听所有事件 * * @param path 路径 * @param listeners {@link FileListener} * @return FileMonitor */ public static FileMonitor create(String path, FileListener... listeners) throws IOException { return create(Paths.get(path), listeners); } /** * 创建并初始化监听,监听所有事件 * * @param path 路径 * @param listeners {@link FileListener} * @return FileMonitor */ public static FileMonitor create(Path path, FileListener... listeners) throws IOException { return new FileMonitor(path, listeners); } //------------------------------------------------------ Static method end //------------------------------------------------------ Constructor method start /** * 构造 * * @param file 文件 * @param listeners 监听器 */ public FileMonitor(File file, FileListener... listeners) throws IOException { this(file.toPath(), listeners); } /** * 构造 * * @param path 字符串路径 * @param listeners 监听器 */ public FileMonitor(String path, FileListener... listeners) throws IOException { this(Paths.get(path), listeners); } /** * 构造 * * @param path 字符串路径 * @param listeners 监听器 */ public FileMonitor(Path path, FileListener... listeners) throws IOException { this.listeners = (null == listeners || listeners.length < 1) ? new CopyOnWriteArrayList<>() : Arrays.asList(listeners); this.path = path; this.init(); } //------------------------------------------------------ Constructor method end @FunctionalInterface interface Action { /** * 事件处理,通过实现此方法处理各种事件。 * 事件可以调用 {@link WatchEvent#kind()}获取 * * @param event 事件 * @param currentPath 事件发生的当前Path路径 */ void doAction(WatchEvent event, Path currentPath); } /** * 初始化
* 初始化包括: *

     * 1、解析传入的路径,判断其为目录还是文件
     * 2、创建{@link WatchService} 对象
     * 
*/ private void init() throws IOException { // 获取目录或文件路径 if (!Files.exists(this.path, LinkOption.NOFOLLOW_LINKS)) { // 不存在的路径 final Path lastPathEle = path.subpath(this.path.getNameCount() - 1, this.path.getNameCount()); final String lastPathEleStr = lastPathEle.toString(); // 带有点表示有扩展名,按照未创建的文件对待 // Linux下.d的为目录,排除 if (lastPathEleStr.indexOf('.') > -1 && !lastPathEleStr.regionMatches(true, lastPathEleStr.length() - ".d".length(), ".d", 0, ".d".length())) { this.filePath = this.path; this.path = this.filePath.getParent(); } //创建不存在的目录或父目录 Files.createDirectories(this.path); } else if (Files.isRegularFile(this.path, LinkOption.NOFOLLOW_LINKS)) { // 文件路径 this.filePath = this.path; this.path = this.filePath.getParent(); } //初始化监听 watchService = FileSystems.getDefault().newWatchService(); } public FileMonitor addListener(FileListener... listeners) { this.listeners.addAll(Arrays.asList(listeners)); return this; } public FileMonitor removeListener(FileListener listener) { while (listeners.remove(listener)) { // empty } return this; } @Override public void run() { // 按照层级注册路径及其子路径 try { registerPath(this.path); } catch (IOException e) { throw new RuntimeException(e); } while (running) { // 执行事件获取并处理 watch((event, currentPath) -> { final WatchEvent.Kind kind = event.kind(); for (FileListener listener : listeners) { if (kind == StandardWatchEventKinds.ENTRY_CREATE) { listener.onCreate(event, currentPath); } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) { listener.onModify(event, currentPath); } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) { listener.onDelete(event, currentPath); } else if (kind == StandardWatchEventKinds.OVERFLOW) { listener.onOverflow(event, currentPath); } } }); } } /** * 执行事件获取并处理 * * @param action 监听回调函数,实现此函数接口用于处理WatchEvent事件 */ private void watch(Action action) { WatchKey watchKey; try { watchKey = watchService.take(); } catch (InterruptedException | ClosedWatchServiceException e) { // 用户中断 close(); return; } if (null == watchKey) { return; } final Path currentPath = watchKeyPathMap.get(watchKey); for (WatchEvent event : watchKey.pollEvents()) { // 如果监听文件,检查当前事件是否与所监听文件关联 if (!(null == filePath || filePath.endsWith(event.context().toString()))) { continue; } action.doAction(event, currentPath); } watchKey.reset(); } /** * 将指定路径加入到监听中 * * @param path 路径 */ public void registerPath(Path path, int maxDepth) throws IOException { final WatchKey watchKey = path.register(this.watchService, // 事件丢失 StandardWatchEventKinds.OVERFLOW, // 修改事件 StandardWatchEventKinds.ENTRY_MODIFY, // 创建事件 StandardWatchEventKinds.ENTRY_CREATE, // 删除事件 StandardWatchEventKinds.ENTRY_DELETE ); watchKeyPathMap.put(watchKey, path); // 递归注册下一层层级的目录 if (maxDepth > 1) { Files.walkFileTree(path, EnumSet.noneOf(FileVisitOption.class), maxDepth, new SimpleFileVisitor() { @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { registerPath(dir, 0);//继续添加目录 return super.postVisitDirectory(dir, exc); } }); } } public void registerPath(URI uri, int maxDepth) throws IOException { registerPath(Paths.get(uri), maxDepth); } public void registerPath(URL url, int maxDepth) throws URISyntaxException, IOException { registerPath(url.toURI(), maxDepth); } public void registerPath(String path, int maxDepth) throws IOException { registerPath(Paths.get(path), maxDepth); } public void registerPath(File file, int maxDepth) throws IOException { registerPath(file.toPath(), maxDepth); } public void registerPath(Path path) throws IOException { registerPath(path, Integer.MAX_VALUE); } public void registerPath(String path) throws IOException { registerPath(Paths.get(path)); } public void registerPath(File file) throws IOException { registerPath(file.toPath()); } public void registerPath(URI uri) throws IOException { registerPath(Paths.get(uri)); } public void registerPath(URL url) throws URISyntaxException, IOException { registerPath(url.toURI()); } // --------------------------------------- @Override public void start() { if (running) { throw new IllegalStateException("Monitor is already running"); } this.running = true; super.start(); } /** * 关闭监听 */ @Override public void close() { running = false; try { watchService.close(); } catch (Exception e) { // 静默关闭 } } // --------------------------------------- }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy