4

对于我想在文件系统中更改条目时更新条目的媒体库,我想通过使用此示例尝试“新”java.nio文件监视功能。 我希望在创建、移动(重命名)或删除文件时获得有用的事件,但这是在 windows7 上观看文件夹时发生的情况(尚未尝试其他操作系统):

格式:
[ThreadName] DEBUG 2012-04-09 18:20:35.934 GroupNumber-COMMAND:路径
ThreadName:每个监视文件夹在其自己的线程中运行,具有不同的 id
GroupNumber:使用相同 GroupNumber 发送的消息同时发送(通常..)
COMMAND:接收到的命令
Path:接收到的路径

Rename:
[Watch0] DEBUG 2012-04-09 18:20:35.934 2-ENTRY_DELETE: C:\tmp\tmp\test.avi
[Watch0] DEBUG 2012-04-09 18:20:35.935 2-ENTRY_CREATE: C:\tmp\tmp\test1.avi
[Watch0] DEBUG 2012-04-09 18:20:35.936 3-ENTRY_MODIFY: C:\tmp\tmp
[Watch0] DEBUG 2012-04-09 18:20:35.937 4-ENTRY_MODIFY: C:\tmp\tmp\test1.avi

[Watch4] DEBUG 2012-04-09 18:43:47.965 18-ENTRY_DELETE: F:\tmp\test.avi
[Watch4] DEBUG 2012-04-09 18:43:47.966 18-ENTRY_CREATE: F:\tmp\test1.avi
[Watch4] DEBUG 2012-04-09 18:43:47.967 19-ENTRY_MODIFY: F:\tmp\test1.avi

Create:
[Watch0] DEBUG 2012-04-09 18:22:02.055 5-ENTRY_CREATE: C:\tmp\test.avi
[Watch0] DEBUG 2012-04-09 18:22:02.066 6-ENTRY_MODIFY: C:\tmp\test.avi
[Watch0] DEBUG 2012-04-09 18:22:03.460 7-ENTRY_MODIFY: C:\tmp\test.avi
//Note the 1.4'' delay between the last two messages. 
//This is the time required to actually copy the file

Move in same watch folder:
[Watch0] DEBUG 2012-04-09 18:18:42.395 0-ENTRY_DELETE: C:\tmp\test.avi
[Watch0] DEBUG 2012-04-09 18:18:42.396 0-ENTRY_MODIFY: C:\tmp\tmp
[Watch0] DEBUG 2012-04-09 18:18:42.396 1-ENTRY_CREATE: C:\tmp\tmp\test.avi
[Watch0] DEBUG 2012-04-09 18:18:42.396 1-ENTRY_MODIFY: C:\tmp\tmp\test.avi

Move to other watch folder on same drive:
[Watch1] DEBUG 2012-04-09 18:23:24.341 8-ENTRY_CREATE: C:\tmp2\test.avi
[Watch0] DEBUG 2012-04-09 18:23:24.341 8-ENTRY_DELETE: C:\tmp\test.avi
[Watch1] DEBUG 2012-04-09 18:23:24.342 10-ENTRY_MODIFY: C:\tmp2\test.avi
//The two 8 are lying. Both messages are being sent from different threads
//and the shared counter hasn't been incremented by any yet. The next entry has been 
//incremented by two!

Move to other watch folder on different drive:
[Watch4] DEBUG 2012-04-09 18:25:42.324 11-ENTRY_CREATE: F:\tmp\test.avi
[Watch4] DEBUG 2012-04-09 18:25:42.338 12-ENTRY_MODIFY: F:\tmp\test.avi
[Watch4] DEBUG 2012-04-09 18:25:42.703 13-ENTRY_MODIFY: F:\tmp\test.avi
[Watch3] DEBUG 2012-04-09 18:25:49.433 14-ENTRY_DELETE: C:\tmp2\test.avi
//Note that the last delete message is being sent from another thread then the first ones.
//This is because the source and destination WatchDirs aren't the same

Delete:
[Watch9] DEBUG 2012-04-05 21:22:02.921 ENTRY_DELETE: C:\tmp\test (2011).mkv

有一组必须解释的“命令+路径”,而不是单个事件。例如,删除由单个命令组成,而重命名和“在同一文件夹中移动”也以删除命令开头,但将由它们未来的命令定义。此外,例如可以并行移动多个文件,这将最终出现在属于不同操作的命令的随机列表中,必须以某种方式对其进行排序。

我能想到的最好的是这个类,其中事件在被接收时被排入队列,然后在另一个线程中被接收后检查片刻(1s)(如果正在生成其他事件并且属于相同的事件,则给一些时间'事件组')。

这适用于重命名、移动、创建或删除单个文件,但如果开始并行复制多个文件或复制一批文件,则不再有效。

我需要的任何实现是否已经存在(似乎是一个常见的用例)?或者有人知道如何解决这个问题以涵盖所有情况?

最后,它必须适用于 windows、linux 和 osx。

一个更复杂的例子,也应该支持

[Watch0] DEBUG 2012-04-09 19:10:17.774 0-ENTRY_CREATE: C:\tmp\tmp\testlarge.avi
[Watch0] DEBUG 2012-04-09 19:10:17.825 0-ENTRY_MODIFY: C:\tmp\tmp\testlarge.avi
[Watch0] DEBUG 2012-04-09 19:10:17.826 1-ENTRY_MODIFY: C:\tmp\tmp
[Watch0] DEBUG 2012-04-09 19:12:09.516 2-ENTRY_DELETE: C:\tmp\tmp\testsmall.avi
[Watch0] DEBUG 2012-04-09 19:12:09.516 3-ENTRY_CREATE: C:\tmp\testsmall.avi
[Watch0] DEBUG 2012-04-09 19:12:09.517 3-ENTRY_MODIFY: C:\tmp\testsmall.avi
[Watch0] DEBUG 2012-04-09 19:12:09.521 4-ENTRY_MODIFY: C:\tmp\tmp
[Watch0] DEBUG 2012-04-09 19:14:13.025 5-ENTRY_MODIFY: C:\tmp\tmp\testlarge.avi

在这里,一个小文件正在被移动,而一个大文件正在被创建。

4

3 回答 3

3

我看到的一件事是您为每个事件创建一个新线程,并使用 na 锁定对象来同步您的线程。

如果您创建一个 ExecutorService 会更好,即重写您的代码,例如:

private ExecutorService executorService;


private class WatchEventHandler implements Callable {

    WatchEvent<?> event;

    public WatchEventHandler(final WatchEvent<?> event) {
        this.event = event;
    }

    @Override
    public void call() throws Exception{
        // do something with the event

        // fireFileWatchAction();


    }    
}


public ManagedFolderWatcher(DOManagedFile managedFolder) throws IOException {
   executorService = Executors.newFixedThreadPool(10);
   ...
}

并在您的运行/处理事件方法中

public void run() {
    try {
       while(true) {

          WatchKey key = watcher.take();
          ....

          for (WatchEvent<?> event : key.pollEvents()) {
              ...
              // in my oppinon there is no need to delay the event 
              executorService.schedule(new WatchEventHandler(event));
          }
          ...

       }
    } catch (InterruptedException ie) {
        // todo: propper error handling
        log.error("Thread interrupted", ie);
    } catch (ClosedWatchServiceException cwse) {
        // todo: propper error handling
        log.error("WatchService allready closed.", cwse);
    }

} 

当您以这种方式实现它时,您甚至不需要您的锁定对象eventEntrySyncObj,并且您的 Callable/Command 具有触发 fileWatchAction 事件所需的所有信息。

于 2012-04-08T18:56:26.507 回答
3

对 java nio 文件观察器不是很熟悉,但我使用jnotify 库从文件系统中获取事件,它非常好,并且可以与 windows 和 linux 一起使用。

如果您想以任何方式使用 nio,请尝试查看此处:https ://blogs.oracle.com/thejavatutorials/entry/watching_a_directory_for_changes

于 2012-04-08T15:20:39.690 回答
3

你得到的事件就是你将得到的事件。在 Windows 上。在 Linux 上,当您查看文件夹时,您只会获取该文件夹的事件,而不是该文件夹中的文件。我相信它在 OS X 上更加粗糙,尽管我从未在那里处理过它。这意味着如果你在看/tmp,你会看到ENTRY_CREATE /tmp/tmp/但不是ENTRY_CREATE /tmp/tmp/test.avi

我通过查看文件夹,在 Set 中累积更改通知,然后定期对 Set 中提到的所有文件进行操作来处理类似的问题。(有一个 Set 正在更新,另一个 Set 正在扫描和清除,我会交换哪个 Set 是哪个。)

简而言之,我放弃了试图弄清楚文件何时被移动、复制或附加。我只是将每次创建或修改都视为创建与先前文件无关的新文件。在实践中,其他任何事情都是行不通的,至少在我的情况下,我正在跟踪服务器上的数百万个文件。

于 2012-04-19T03:31:41.497 回答