java—在访问事件文件之前等待watchservice事件处理完成

gorkyyrv  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(347)

我正在使用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");
        }

如您所见,我正在等待系统写入文件,然后才能访问fileinput流。我觉得这样做不对,我想知道是否有更好的方法来实现这一点,然后调用thread.sleep()。
谢谢你的时间。

igetnqfo

igetnqfo1#

watchservice可以将许多事件一起发送给您,甚至可以为同一个文件提供“创建然后修改”事件,或者为正在其他地方写入的文件提供“删除”、“创建”、“修改”所有相同的事件列表(如文本编辑器重写)。
防止中间进程写入和报告的最简单方法是等待watch服务暂停。你的逻辑需要 ws.take() 用于第一个查询,然后 ws.poll(1) 如果您已经收到事件:

WatchKey wk = hasPending ? ws.poll(1,TimeUnit.MILLISECONDS) : ws.take();

这是来自一个测试程序的片段,它整理所有事件,并在最后一个程序没有返回监视结果时对它们执行操作 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();
    }
}

如果你只是在捕捉事件 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的事件处理程序与写入操作冲突或错过文件结尾的可能性。

相关问题