重命名目录后 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
该代码会注册使用监视服务创建的任何新目录以侦听所有事件 并运行 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