2015-03-31 64 views
1

我试图在java WatchService中实现简单的重命名。在java watchservice中实现重命名和删除

我的假设是:当文件被重命名,三个操作执行删除文件XXX的

  • 创建文件YYY的
  • 文件YYY的修改

下面是我的代码:

MyWatcher.java

import java.io.IOException; 
    import java.nio.file.FileSystems; 
    import java.nio.file.Path; 
    import java.nio.file.Paths; 
    import java.nio.file.StandardWatchEventKinds; 
    import java.nio.file.WatchEvent; 
    import java.nio.file.WatchKey; 
    import java.nio.file.WatchService; 
    import java.util.ArrayList; 
    import java.util.List; 

    public class MyWatcher { 

     @SuppressWarnings("rawtypes") 
     public static void main(String[] strings) { 

      Path myWatchPath = Paths.get("D:\\log4j"); 
      long preventDuplicateTime = 0; 
      FileDelete onDelete = new FileDelete();//this object must be thread safe 
      List<String> notifications = new ArrayList<String>(); 

      WatchService myPathWatchService = null; 
      try { 
       myPathWatchService = FileSystems.getDefault().newWatchService(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      try { 
       myWatchPath.register(myPathWatchService, 
         StandardWatchEventKinds.ENTRY_CREATE, 
         StandardWatchEventKinds.ENTRY_DELETE, 
         StandardWatchEventKinds.ENTRY_MODIFY); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      boolean isKeyValid = true; 
      while (isKeyValid) { 
       WatchKey myPathWatchKey = null; 
       try { 
        myPathWatchKey = myPathWatchService.take(); 
       } catch (InterruptedException e) { 
        e.printStackTrace();// throw 
       } 
        for (WatchEvent watchEvent : myPathWatchKey.pollEvents()) { 
         //WatchEvent.Kind kind = watchEvent.kind(); 
         if (StandardWatchEventKinds.ENTRY_CREATE.equals(watchEvent 
           .kind())) { 
          String fileName = watchEvent.context().toString(); 
          if(onDelete.status == -1) 
          System.out.println("File Created:" + fileName + " " 
            + watchEvent.context()); 
          else{ 
           if(onDelete.status == 0){ 
            onDelete.createdTime = System.nanoTime(); 
           if (onDelete.deletedTime/10000000 == onDelete.createdTime/10000000) { 
            onDelete.createdFile = watchEvent.context().toString(); 
            onDelete.status++; 
            notifications.add("File Created:" + fileName); 
           }else{ 
            for (String string : notifications) { 
             System.out.println(string); 
            } 
            notifications = new ArrayList<String>(); 
            System.out.println("File Created:" + fileName + " " 
              + watchEvent.context()); 
            onDelete = new FileDelete(); //Time duration not close (seems not renamed) 
           } 
           }else{ 
            //this should never come here!! 
            onDelete = new FileDelete(); 
           } 
          } 
         } 
         if (StandardWatchEventKinds.ENTRY_DELETE.equals(watchEvent 
           .kind())) { 
          String fileName = watchEvent.context().toString(); 
          if(onDelete.status == -1){ 
           onDelete = new FileDelete(); 
           onDelete.status++; 
           onDelete.deletedFile = watchEvent.context().toString(); 
           onDelete.deletedTime = System.nanoTime(); 
           notifications.add("File deleted:" + fileName); 
          } 
          //System.out.println("File deleted:" + fileName); // push to notfication to array for later use 
         } 
         if (StandardWatchEventKinds.ENTRY_MODIFY.equals(watchEvent 
           .kind())) { 
          long current = System.nanoTime(); 
          String fileName = watchEvent.context().toString(); 
          if(!(preventDuplicateTime/10000000 == current/10000000)) 
           notifications.add("File modified:" + fileName); 
          preventDuplicateTime = (System.nanoTime()); 
          onDelete.modifiedFile= fileName; 
          onDelete.modifiedTime =System.nanoTime(); 
          if(onDelete.status != 1){ 
           for (String messages : notifications) { 
            System.out.println(messages); 
           } 
          onDelete= new FileDelete(); 
          notifications = new ArrayList<String>(); 
          } 
          else if(onDelete.createdFile.equals(onDelete.modifiedFile)) 
            if(onDelete.createdTime /10000000 == onDelete.modifiedTime/10000000){ 
             System.out.println("File renamed:" + fileName); 
             onDelete = new FileDelete(); 
             notifications = new ArrayList<String>(); 
          } 
         } 
        /*}*/ 

       } 
       isKeyValid = myPathWatchKey.reset(); 
      } 
     } 
    } 

FileRename.java

public class FileRename { 
    int status =-1; 
    String deletedFile = ""; 
    long deletedTime = 0 ; 
    String createdFile = ""; 
    long createdTime =0 ; 
    String modifiedFile = ""; 
    long modifiedTime = 0 ; 
} 

它完美呈现在重命名操作,但问题是我不明白如何显示onDelete。因为每个删除都被推入通知!或者帮助我实现重命名!

*请注意不要建议第三方罐子! (因为它们中的大多数,像JNotify一样,取决于操作系统)

+0

对我来说,这似乎是不可能的,如果你不看这些文件本身(也许保持的FileKey()和比较它在捕获的事件中)。从目录的角度来看,你将如何区分'delete file1 + create file2'和'delete file1 + create file1_with_new_name'?在Linux上,inode可以告诉你它是否是同一个文件。关于javadoc fileKey()将使用inode进行标识。 – SubOptimal 2015-03-31 13:11:22

+0

@SubOptimal我的目标是观看单个目录...对不起,我没有得到你你可以请详细说明 – theRoot 2015-03-31 13:21:19

+0

@SubOptimal似乎inode是特定于操作系统我想要通用的解决方案,这可能吗? – theRoot 2015-03-31 15:11:09

回答

5

请在下面找到解释为什么独立于操作系统的解决方案不起作用的解释。以及为什么Java WatchService的事件粒度太弱,无法实现。

根据可用事件(CREATE,MODIFY,DELETE),您无法确定发生了哪个操作。

采取下面的例子在Linux机器上

创建一些测试文件

touch /tmp/stackoverflow/foo /tmp/stackoverflow/foo2 

执行以下命令

rm foo && cp foo2 bar && echo foo > bar 

这将创建下列事件(监控与WatchDir.java

ENTRY_DELETE ..:..:.. [.........]: /tmp/stackoverflow/foo 
ENTRY_CREATE 20:09:37 [rw.rw.rw.]: /tmp/stackoverflow/bar 
ENTRY_MODIFY 20:09:37 [rw.rw.rw.]: /tmp/stackoverflow/bar 

在您承担事件订单之后,这应该是rename操作。

Wheras mv bar foobar创建以下事件。

ENTRY_DELETE ..:..:.. [.........]: /tmp/stackoverflow/bar 
ENTRY_CREATE 19:55:37 [rw.rw.rw.]: /tmp/stackoverflow/foobar 

现在同样的一个窗口机

创建一些测试文件

rem.>c:/temp/stackoverflow/foo 
rem.>c:/tmp/stackoverflow/foo2 

执行以下命令

del foo 
copy foo2 bar 

这将创建下列事件

ENTRY_DELETE ..:..:.. [.........]: c:\temp\stackoverflow\foo 
ENTRY_CREATE 19:59:10 [.........]: c:\temp\stackoverflow\bar 
ENTRY_MODIFY 19:59:10 [.........]: c:\temp\stackoverflow\bar 

在您假设事件顺序后,这应该是rename操作。

Wheras ren bar foobar在这种情况下创建相同的事件顺序。

ENTRY_DELETE ..:..:.. [.........]: c:\temp\stackoverflow\bar 
ENTRY_CREATE 20:02:41 [.........]: c:\temp\stackoverflow\foobar 
ENTRY_MODIFY 20:02:41 [.........]: c:\temp\stackoverflow\foobar 

与Linux机器上的iwatch /tmp/stackoverflow/相反,您可以准确确定发生了什么。

执行命令rm foo && cp foo2 bar && echo foo > bar 会产生下列inotify事件。

[31/Mär/2015 20:25:40] IN_DELETE /tmp/stackoverflow//foo 
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//foo is deleted 
[31/Mär/2015 20:25:40] IN_CREATE /tmp/stackoverflow//bar 
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/stackoverflow//bar 
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//bar is closed 
[31/Mär/2015 20:25:40] IN_CLOSE_WRITE /tmp/stackoverflow//bar 
[31/Mär/2015 20:25:40] * /tmp/stackoverflow//bar is closed 

mv bar foobar创建以下inotify事件

[31/Mär/2015 20:27:10] IN_MOVED_FROM /tmp/stackoverflow//bar 
[31/Mär/2015 20:27:10] IN_MOVED_TO /tmp/stackoverflow//foobar 
[31/Mär/2015 20:27:10] * /tmp/stackoverflow//bar is moved to /tmp/stackoverflow//foobar 

编辑相关的Java源代码,以确认我的解释。没有办法(用普通的Java)找出一个文件是否被重命名,或者一个文件被删除,另一个文件被创建(同时)。

WindowsWatchService.java线462

// Translate file change action into watch event 
private WatchEvent.Kind<?> translateActionToEvent(int action) 
{ 
    switch (action) { 
     case FILE_ACTION_MODIFIED : 
      return StandardWatchEventKinds.ENTRY_MODIFY; 

     case FILE_ACTION_ADDED : 
     case FILE_ACTION_RENAMED_NEW_NAME : 
      return StandardWatchEventKinds.ENTRY_CREATE; 

     case FILE_ACTION_REMOVED : 
     case FILE_ACTION_RENAMED_OLD_NAME : 
      return StandardWatchEventKinds.ENTRY_DELETE; 

     default : 
      return null; // action not recognized 
    } 
} 

LinuxWatchService.java线376

/** 
* map inotify event to WatchEvent.Kind 
*/ 
private WatchEvent.Kind<?> maskToEventKind(int mask) { 
    if ((mask & IN_MODIFY) > 0) 
     return StandardWatchEventKinds.ENTRY_MODIFY; 
    if ((mask & IN_ATTRIB) > 0) 
     return StandardWatchEventKinds.ENTRY_MODIFY; 
    if ((mask & IN_CREATE) > 0) 
     return StandardWatchEventKinds.ENTRY_CREATE; 
    if ((mask & IN_MOVED_TO) > 0) 
     return StandardWatchEventKinds.ENTRY_CREATE; 
    if ((mask & IN_DELETE) > 0) 
     return StandardWatchEventKinds.ENTRY_DELETE; 
    if ((mask & IN_MOVED_FROM) > 0) 
     return StandardWatchEventKinds.ENTRY_DELETE; 
    return null; 
} 
+0

感谢您的参考解释! – theRoot 2015-04-03 04:21:46