我们在做数据接入或者文件解析的时候,经常需要对目录下的文件进行监控和解析。在对文件监控时,以前的做法是通过定时轮询该目录下,获取该目录的文件,该方法会有延时。从 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);
}