重命名目录后 WatchService 目录路径不正确

WatchService Directory path incorrect after renaming Directory

该代码会注册使用监视服务创建的任何新目录以侦听所有事件 并运行 60 秒的持续时间[可以用 运行 时间更改]

final class Test 
{
 public static void main(String[] args)throws Exception
 {
  //Run the program for 60 seconds i.e * 1000
  long RUNNING_TIME=60*1000;
  
  try(WatchService service=FileSystems.getDefault().newWatchService())
  {
   //Register test directory for observation
   Path.of("E:","Folder A").register(service,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_DELETE);
   
   long start=System.currentTimeMillis();
   while(System.currentTimeMillis()-start<RUNNING_TIME)
   {
    //Use poll not take else while loop will be blocked
    WatchKey key=service.poll();
    
    if(key==null){continue;}
    for(WatchEvent event:key.pollEvents())
    {
    //Get the absolute path from the event context by resolving it against it's parent directory
     Path
     eventPath=(Path)event.context(),
     absolutePath=((Path)key.watchable()).resolve(eventPath); //Wrong path after resolving against watchable after renaming directory
     Kind kind=event.kind();
     
     //If the event kind is CREATE and an new directory is created then register that directory with the service
     if(kind==StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(absolutePath))
     {
      absolutePath.register
      (
       service,
       StandardWatchEventKinds.ENTRY_CREATE,
       StandardWatchEventKinds.ENTRY_MODIFY,
       StandardWatchEventKinds.ENTRY_DELETE
      );
     }
     
     //Debugging
     System.out.println("Event Path="+eventPath);
     System.out.println("Absolute Path="+absolutePath);
     System.out.println("Kind="+kind);
     System.out.println("==========================");
    }
   
    key.reset();
   }  
  }
 }
}

这是观察到的结果

Sno                            Test                                  Output

1       Create Directory=>E:/Folder A/New Folder               Event Path=New folder
                                                          Absolute Path=E:\Folder A\New folder
                                                                 Kind=ENTRY_CREATE
                                                             ==========================



2        Rename E:/Folder A/New Folder                         Event Path=New folder
                       TO                                 Absolute Path=E:\Folder A\New folder
                     E:/Folder A/Test                           Kind=ENTRY_DELETE
                                                            ==========================
                                                               Event Path=Test
                                                         Absolute Path=E:\Folder A\Test
                                                               Kind=ENTRY_CREATE



3        Create Text File Under Test Folder i.e            Event Path=New Text Document.txt
     Create => E:/Folder A/Test/New Text Document.txt     Absolute Path=E:\Folder A\New folder\New Text Document.txt
                                                             Kind=ENTRY_CREATE
                                                            ==========================
                                                             Event Path=Test
                                                          Absolute Path=E:\Folder A\Test
                                                             Kind=ENTRY_MODIFY




4    Rename  E:/Folder A/Test/New Text Document.txt        Event Path=New Text Document.txt
                   To                                 Absolute Path=E:\Folder A\New folder\New Text Document.txt
            E:/Folder A/Test/Test.txt                      Kind=ENTRY_DELETE
                                                        ==========================
                                                          Event Path=Test.txt
                                                      Absolute Path=E:\Folder A\New folder\Test.txt
                                                           Kind=ENTRY_CREATE                                                          
                                                        ==========================
                                                            Event Path=Test
                                                      Absolute Path=E:\Folder A\Test
                                                          Kind=ENTRY_MODIFY
                                                        

绝对路径中的测试用例 3 出现不正确的结果 这是从测试用例 3 开始的不正确的绝对路径值

Absolute Path=E:\Folder A\New folder\New Text Document.txt
Absolute Path=E:\Folder A\New folder\Test.txt

如您所见,在测试用例 2 中,我已将我的文件夹从新文件夹重命名为测试,那么为什么在针对可观察目录解析时它仍然出现。

显然错误出在从 watchable() 返回的 Path 中,或者我可能应该在重命名或其他一些情况下取消此键以获得正确的结果。

为什么 watchable() 方法在重命名目录后没有返回正确的路径?

如果为重命名的目录取消注册以前的手表设置,则可以避免此问题。重命名时,父文件夹watch先DELETE然后CREATE,这样就可以修改watched目录,取消旧key,注册新key。

这是您的主要内容,稍作调整以记录使用中的密钥并使用 HashMap 处理删除 + 创建以跟踪添加到监视服务的目录:

var keys = new HashMap<Path,WatchKey>();

try (WatchService service = FileSystems.getDefault().newWatchService()) {
    // Register test directory for observation
    Path root = Path.of("C:/Temp/Folder A");

    WatchKey wk = root.register(service, StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
    System.out.println("ROOT register=" + root +" wk="+wk);

    long start = System.currentTimeMillis();
    while (System.currentTimeMillis() - start < RUNNING_TIME) {
        // Use poll not take else while loop will be blocked
        WatchKey key = service.poll();

        if (key == null) {
            continue;
        }
        for (var event : key.pollEvents())  {
            // Get the absolute path from the event context by resolving it against it's
            // parent directory
            Path eventPath = (Path) event.context(), absolutePath = ((Path) key.watchable()).resolve(eventPath);

            var kind = event.kind();

            // Debugging
            System.out.println("==========================");
            System.out.println("Watch Key=" + key);
            System.out.println("Event Path=" + eventPath);
            System.out.println("Absolute Path=" + absolutePath);
            System.out.println("Kind=" + kind);

            // If the event kind is CREATE and an new directory is created then register
            // that directory with the service
            if (kind == StandardWatchEventKinds.ENTRY_CREATE && Files.isDirectory(absolutePath)) {
                var subkey = absolutePath.register(service, StandardWatchEventKinds.ENTRY_CREATE,
                        StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE);
                keys.put(absolutePath, subkey);
                System.out.println("register=" + absolutePath +" wk="+subkey);
            }
            // Deletes: wipe previously registered watch
            else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                var prev = keys.remove(absolutePath);
                if (prev != null) {
                    System.out.println("unregister=" + absolutePath + " key="+prev);
                    prev.cancel();
                }
            }
        }

        key.reset();
    }
}

如上,开始再创建“子文件夹”会报三个不同的watch keys:

ROOT register=C:\Temp\Folder A wk=... $WindowsWatchKey@aaaaaaaaa

Watch Key=... $WindowsWatchKey@aaaaaaaaa
Kind=ENTRY_CREATE
register=C:\Temp\Folder A\subfolder wk=... $WindowsWatchKey@xxxxxxxx

将“子文件夹”重命名为“最新”说:

Watch Key=... $WindowsWatchKey@aaaaaaaaa
Kind=ENTRY_DELETE
unregister=C:\Temp\Folder A\subfolder key=... $WindowsWatchKey@xxxxxxxx

Watch Key=... $WindowsWatchKey@aaaaaaaaa
Kind=ENTRY_CREATE
register=C:\Temp\Folder A\LATEST wk=... $WindowsWatchKey@yyyyyyyyy

然后在新目录名下编辑文件将报告当前文件夹名,最重要的是,重命名后新的 watch key 而不是之前打印的 @xxxxxxxx 值:

Watch Key=sun.nio.fs.WindowsWatchService$WindowsWatchKey@yyyyyyyyy
Event Path=file.txt
Absolute Path=C:\Temp\Folder A\LATEST\file.txt
Kind=ENTRY_MODIFY