在java watchservice中实现重命名和删除

在java watchservice中实现重命名和删除,java,file,directory,listener,watchservice,Java,File,Directory,Listener,Watchservice,我尝试在JavaWatchService中实现简单的重命名 我的假设是:重命名文件时,将执行三个操作 删除文件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

我尝试在JavaWatchService中实现简单的重命名

我的假设是:重命名文件时,将执行三个操作

  • 删除文件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();
            }
        }
    }
public class FileRename {
    int status =-1;
    String deletedFile = "";
    long deletedTime = 0 ;
    String createdFile = "";
    long createdTime =0 ;
    String modifiedFile = "";
    long modifiedTime = 0 ;
}
它在重命名操作中显示得非常完美,但问题是我不知道如何显示onDelete。因为每次删除都会被推送到通知中!!或者帮我实现重命名


*注意:请不要建议使用第三方罐子!(因为它们中的大多数,如JNotify,都依赖于操作系统)

请在下面找到一些解释,说明为什么独立于操作系统的解决方案不起作用。以及为什么Java
WatchService
的事件粒度对于您想要实现的目标来说太弱

根据可用事件(创建、修改、删除),您无法确定发生了哪些操作

以Linux机器上的以下示例为例

创建一些测试文件

touch /tmp/stackoverflow/foo /tmp/stackoverflow/foo2
rm foo && cp foo2 bar && echo foo > bar
rem.>c:/temp/stackoverflow/foo
rem.>c:/tmp/stackoverflow/foo2
del foo
copy foo2 bar
执行以下命令

touch /tmp/stackoverflow/foo /tmp/stackoverflow/foo2
rm foo && cp foo2 bar && echo foo > bar
rem.>c:/temp/stackoverflow/foo
rem.>c:/tmp/stackoverflow/foo2
del foo
copy foo2 bar
这将创建以下事件(使用监控)

根据您对事件顺序的假设,这将是一个
重命名
操作

Wheras
mv bar foobar
创建以下事件

ENTRY_DELETE ..:..:.. [.........]: /tmp/stackoverflow/bar
ENTRY_CREATE 19:55:37 [rw.rw.rw.]: /tmp/stackoverflow/foobar
现在同样适用于开窗机

创建一些测试文件

touch /tmp/stackoverflow/foo /tmp/stackoverflow/foo2
rm foo && cp foo2 bar && echo foo > bar
rem.>c:/temp/stackoverflow/foo
rem.>c:/tmp/stackoverflow/foo2
del foo
copy foo2 bar
执行以下命令

touch /tmp/stackoverflow/foo /tmp/stackoverflow/foo2
rm foo && cp foo2 bar && echo foo > bar
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
根据您对事件顺序的假设,这将是一个
重命名
操作

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中,无法确定是重命名了一个文件,还是删除了一个文件,同时创建了另一个文件

第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
    }
}
//将文件更改操作转换为监视事件
私有WatchEvent.Kind translateActionToEvent(int操作)
{
开关(动作){
案例文件\行动\修改:
返回StandardWatchEventTypes.ENTRY\u MODIFY;
案例文件_行动_添加:
案例文件\u操作\u重命名\u新\u名称:
返回StandardWatchEventTypes.ENTRY\u创建;
案例文件\u操作\u已删除:
案例文件\u操作\u重命名\u旧\u名称:
返回StandardWatchEventTypes.ENTRY\u DELETE;
违约:
返回null;//无法识别操作
}
}
第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;
}
/**
*将inotify事件映射到WatchEvent.Kind
*/
私有WatchEvent.Kind maskToEventKind(int mask){
如果((屏蔽和插入修改)>0)
返回StandardWatchEventTypes.ENTRY\u MODIFY;
如果((屏蔽和输入属性)>0)
返回StandardWatchEventTypes.ENTRY\u MODIFY;
如果((屏蔽和插入创建)>0)
返回StandardWatchEventTypes.ENTRY\u创建;
如果((屏蔽和插入移动到)>0)
返回StandardWatchEventTypes.ENTRY\u创建;
如果((屏蔽和插入删除)>0)
返回StandardWatchEventTypes.ENTRY\u DELETE;
如果((掩码和从中移动)>0)
返回StandardWatchEventTypes.ENTRY\u DELETE;
返回null;
}

对于我来说,如果不查看文件本身(可能保留fileKey()并在捕获的事件中进行比较),这似乎是不可能的。从目录的角度看,您将如何区分
删除文件1+创建文件2
删除文件1+使用新名称创建文件1
?在Linux上,inode可以告诉您它是否是同一个文件。关于javadoc fileKey()将使用inode进行标识。@次优我的目标是查看单个目录…对不起,我没有得到您可以详细说明吗it@SubOptimal似乎inode是操作系统特有的,我想要通用的解决方案,有可能吗?看看我编辑的答案。使用普通Java运行时类无法实现您想要的功能。Any1得到了一条线索,说明了为什么它们只是减少这些信息,而不传递它们,因此我们有了更好的粒度?@JayC667我相信不是每个文件系统都支持所有事件。