有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

文件系统使用java。nio在文件更改时引发创建/移动/删除事件

对于一个媒体库,我想在文件系统中的条目发生变化时更新它们,我想给它一个“新”的java。niofile watching features使用this example进行尝试
我希望在创建、移动(重命名)或删除文件时获得有用的事件,但下面是在windows7上观看文件夹时发生的情况(尚未尝试其他操作系统):

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

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

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

我能想到的最好的方法是this class,在这里,事件在接收时排队,然后在另一个线程中接收到事件后的一瞬间(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

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


共 (3) 个答案

  1. # 2 楼答案

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

    如果您创建一个ExecutorService,效果会更好 i、 e.重写您的代码,如:

    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);
       ...
    }
    

    在您的run/process events方法中

    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);
        }
    
    } 
    

    当您以这种方式实现它时,您甚至不需要lock对象eventEntrySyncObj,而且您的Callable/命令具有触发fileWatchAction事件所需的所有信息

  2. # 3 楼答案

    你正在获得的事件就是你将要获得的事件。在窗户上。在Linux上,当您查看一个文件夹时,您只会获取该文件夹的事件,而不会获取该文件夹中的文件的事件。我相信它在OSX上甚至更为粗糙,尽管我从未在那里处理过它。这意味着如果您正在观看/tmp,您将看到ENTRY_CREATE /tmp/tmp/,但不会看到ENTRY_CREATE /tmp/tmp/test.avi

    我处理了一个类似的问题,通过观察文件夹,在一个集合中积累更改通知,然后定期操作集合中提到的所有文件。(有一组正在更新,另一组正在扫描和清除,我会交换哪一组是哪一组。)

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