java - 在访问事件文件之前等待 WatchService 事件完成处理
问题描述
我正在使用java.nio.file.WatchService
;观看目录 每次创建文件时,我都会调用processFile()
它传递文件句柄。
这是观察者的代码:
boolean poll = true;
System.out.println("Watching Directory: "+path.toString()+"...");
while (poll) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent<Path> ev = cast(event);
Path dir = (Path)key.watchable();
Path fullPath = dir.resolve(ev.context());
File file = new File(fullPath.toString());
System.out.println("Event kind:" + event.kind()
+ ". File affected: " + event.context()
+ ". Full path" + fullPath);
if(event.kind() == ENTRY_CREATE) fileProcessor.processFile(file);
}
poll = key.reset();
}
这是 processFile() 代码:
public void processFile(File file) {
String threadName = "Thread-" + file.getName();
Thread t = ProcessorUtil.getThreadByName(threadName);
if (t == null || !t.isAlive()) {
new Thread(() -> {
try {
Thread.sleep(1000); //HACKY WAY of WAITING
InputStream in = new FileInputStream(file);
someProcess(in);
} catch (Exception e) {
_log.error("Exception: ",e);
}
}, threadName).start();
} else {
System.out.println("File "+file.getName()+" currently being processes");
}
如您所见,我正在等待系统写入文件,然后才能访问FileInputStream
. 这对我来说似乎不对,我想知道是否有比调用更好的方法来做到这一点Thread.sleep()
。
解决方案
watchservice 可以将许多事件一起发送给您,甚至可能为同一文件提供 CREATE 然后 MODIFY 事件,或者为正在其他地方写入的文件(例如,文本编辑器重写)在相同的事件列表中删除、创建、修改所有事件。
防止中间进程写入和报告等到监视服务暂停的最简单方法。您的逻辑需要ws.take()
进行第一次查询,然后ws.poll(1)
如果您已经收到事件:
WatchKey wk = hasPending ? ws.poll(1,TimeUnit.MILLISECONDS) : ws.take();
这是一个测试程序的片段,它整理所有事件并在 last 没有返回观察结果时对它们进行操作poll(1)
。
Set<Path> created = new LinkedHashSet<>();
Set<Path> modified = new LinkedHashSet<>();
Set<Path> deleted = new LinkedHashSet<>();
while(appIsRunning) {
boolean hasPending = created.size() + modified.size() + deleted.size() > 0;
WatchKey wk = hasPending ? ws.poll(100,TimeUnit.MILLISECONDS) : ws.take();
if (wk != null) {
for (WatchEvent<?> event : wk.pollEvents()) {
Path parent = (Path) wk.watchable();
Path eventPath = (Path) event.context();
store(event.kind(), parent.resolve(eventPath), created, modified, deleted);
}
boolean valid = wk.reset();
if (!valid) {
throw new RuntimeException("Check the path, it may be deleted: "+dir);
}
}
System.out.println("PENDING: cre="+created.size()+" mod="+modified.size()+" del="+deleted.size());
// ONLY HANDLE EVENTS IF NOTHING WAS RECEIVED:
if (wk == null && hasPending) {
// FIRE EVENTS for each list HERE: deleted, created, modified
// reset the list for next take() cycle:
deleted.clear(); created.clear(); modified.clear();
}
}
如果您只捕获 CREATE 事件store()
,这很容易,此版本将多种类型整理为最新的合理类型:
/**
* Save event for later processing by event kind EXCEPT for:
* <li>DELETE followed by CREATE => store as MODIFY
* <li>CREATE followed by MODIFY => store as CREATE
* <li>CREATE or MODIFY followed by DELETE => store as DELETE
*/
private static void
store(Kind<?> kind, Path path, Set<Path> created, Set<Path> modified, Set<Path> deleted) {
System.out.println("STORE "+kind+" path:"+path);
boolean cre = false;
boolean mod = false;
boolean del = kind == StandardWatchEventKinds.ENTRY_DELETE;
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
mod = deleted.contains(path);
cre = !mod;
}
else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
cre = created.contains(path);
mod = !cre;
}
addOrRemove(created, cre, path);
addOrRemove(modified, mod, path);
addOrRemove(deleted, del, path);
}
// Add or remove from the set:
private static void addOrRemove(Set<Path> set, boolean add, Path path) {
if (add) set.add(path);
else set.remove(path);
}
这减少了 CREATE 的事件处理程序与写入操作发生冲突或错过文件末尾的机会。
推荐阅读
- c++ - 类枚举派生类
- python - 根据 if 语句将列从 excel 文件附加到 csv 文件
- python - 插入名称时的 Psycopg2 错误包含 'into PostgreSQL
- java - 如何在 Java 中创建对象集合 Spark Dataset?
- html - 如何在 *ngFor 中显示索引数据?
- python - 如何使用python按文件类型绘制折线图?
- c - 有没有办法验证我的程序没有内存泄漏?
- foreach - 如何使用逻辑应用在 foreach 循环中创建具有 api 响应的命名 blob
- sql - 在表中管理 Json
- performance - 扩展事件捕获最少的事件以获得更好的性能