基于WatchService实现对目录下文件操作的实时监控

我们在做数据接入或者文件解析的时候,经常需要对目录下的文件进行监控和解析。在对文件监控时,以前的做法是通过定时轮询该目录下,获取该目录的文件,该方法会有延时。从 JDK 1.7 版本开始,提供了 WatchService 接口,基于操作系统原生文件系统,实现对文件创建、修改和删除操作的实时监控。

但是需要注意的是,WatchService 只能对我们编写的程序启动后对文件的操作进行监视,对于目录下的历史文件则无法监视,因此在程序启动时,还需要对历史文件进行处理。

因此,我们封住了一个工具类,实现文件的监视和处理,包括两个类:
- FileWatcher:文件监视器,用于监视文件目录,并将监视到的文件提交给文件解析器进行处理。
- FileListener:文件解析监听接口,监视器根据文件操作类型调用该接口的业务实现类执行处理。

FileWatcher

/**
 * 通过监听文件目录,获取新增的文件并提交处理
 */
public class FileWatcher {
    /**
     * 监视的文件目录
     */
    private Path watchDirectory;
    /**
     * 监视的文件类型正则表达式
     */
    private FilenameFilter filenameFilter;
    /**
     * 监视到的文件监听器
     */
    private FileListener fileListener;

    public FileWatcher(Path watchDirectory, FilenameFilter filenameFilter, FileListener fileListener) {
        this.watchDirectory = watchDirectory;
        this.filenameFilter = filenameFilter;
        this.fileListener = fileListener;
    }

    public FileWatcher(Path watchDirectory, FileListener fileListener) {
        this(watchDirectory, null, fileListener);
    }

    /**
     * 由于监听文件目录并不会处理已经存在与目录中的历史文件,所以需要单独处理
     */
    private void executeHistoryFiles() {
        try {
            Stream<Path> stream = Files.list(this.watchDirectory);
            if (this.filenameFilter != null) {
                stream = stream.filter(path -> {
                    String fileName = path.getFileName().toString();
                    return filenameFilter.accept(this.watchDirectory.toFile(), fileName);
                });
            }
            stream.forEach(path -> {
                this.fileListener.onCreate(path);
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 监听目录,根据文件的变化处理文件
     */
    public void watch() {
        // 先处理历史文件
        executeHistoryFiles();
        // 监听新文件
        try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
            this.watchDirectory.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
            while (true) {
                WatchKey watchKey = watchService.take();
                for (WatchEvent event : watchKey.pollEvents()) {
                    WatchEvent.Kind eventKind = event.kind();
                    if (eventKind == StandardWatchEventKinds.OVERFLOW) {
                        continue;
                    }
                    String fileName = event.context().toString();
                    //文件名不匹配
                    if (this.filenameFilter != null && !this.filenameFilter.accept(this.watchDirectory.toFile(), fileName)) {
                        continue;
                    }
                    Path file = Paths.get(this.watchDirectory.toString(), fileName);
                    if (eventKind == StandardWatchEventKinds.ENTRY_CREATE) {
                        this.fileListener.onCreate(file);
                    } else if (eventKind == StandardWatchEventKinds.ENTRY_MODIFY) {
                        this.fileListener.onModify(file);
                    } else if (eventKind == StandardWatchEventKinds.ENTRY_DELETE) {
                        this.fileListener.onDelete(file);
                    }
                }
                boolean isKeyValid = watchKey.reset();
                if (!isKeyValid) {
                    break;
                }
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

FileListener

public interface FileListener {
    /**
     * 在监视目录中新增文件时的处理操作
     * @param file
     */
    void onCreate(Path file);

    /**
     * 在监视目录中修改文件时的处理操作
     * @param file
     */
    void onModify(Path file);

    /**
     * 在监视目录中删除文件时的处理操作
     * @param file
     */
    void onDelete(Path file);
}

猜你喜欢

转载自blog.csdn.net/twypx/article/details/81218841