我试图在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,都依赖于操作系统)

#1楼 票数:7 已采纳

请在下面找到一些解释为什么独立于操作系统的解决方案无效 为什么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

对于Window机器现在也一样

创建一些测试文件

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

  ask by theRoot translate from so

未解决问题?本站智能推荐:

1回复

在Java 1.7中通过WatchService监视目录更改

从Java 1.7开始,有一种无需添加其他库即可监视目录的方法。 在Oracle网站上有一个小教程,介绍如何使用WatchService http://docs.oracle.com/javase/tutorial/essential/io/notification.html#try,但这对我
1回复

在观察远程目录中的更改时,WatchService.take()不会在Windows-7-x64上等待

我正在尝试使用Java SE 7中提供的WatchService来监视(在网络计算机上)远程目录。当我在Windows XP上运行该程序时,它的工作正常,但是在Windows 7上,WatchService.take()不会等待,返回无效的密钥。 这是示例代码: 这是输出:
14回复

Java 7 WatchService-忽略同一事件的多次出现

StandardWatchEventKinds.ENTRY_MODIFY的Javadoc说: 目录条目已修改。 当为该事件注册目录时,如果观察到目录中的条目已被修改,则WatchKey将排队。 此事件的事件计数为1或更大。 通过编辑器编辑文件的内容时,它将同时修改日期(或其
2回复

WatchService:错过和未处理的事件

我遇到了WatchService的问题。 这是我的代码片段: *首先,要知道watch()是在无限循环中调用的。 问题是,一次创建多个文件时,会丢失一些事件。 例如,如果我将三个文件复制粘贴到“... / request”文件夹中,则只有一个文件被捕获,其他文件仍然没有发生,也
1回复

观看目录更改Java.nio.file

我知道java.nio.file可以提供监视文件更改的方法,例如新文件,修改和删除。 但是现在我想知道是否有一种方法可以查看是否由某些应用程序(例如编辑器)输入了目录或正在打开一个文件。 我已经阅读了API文档,无法找到实现此目的的方法。 任何人都可以提供有关此方面的线索,也许是其他A
1回复

用于文件的Java监视文件夹

我需要监视某个文件夹中的新文件,我需要处理它。 我有以下要求: 文件的文件名是序列号。 我需要按顺序处理每个文件。 (最低编号首先,不能保证每个序列号都存在。例如:1,2,5,8,9 如果启动期间文件夹中已存在文件,我需要直接处理它们 我需要保证我只处理每个文件一
2回复

获取有关Java中已更改文件的信息

我正在尝试监视Java中的文件。 我想知道何时进行文件修改: 哪个过程进行了更改。 发生了什么变化。 另外,我知道有一种方法可以更改文件中的“上次修改日期”,因此我想检查是否有人更改了该字段。 我尝试了“ commons.io”,“ DefaultFileMon
1回复

Java7 WatchService-如何检测实际监视目录的重命名/移动

我正在使用WatchService与应用程序工作台同步数据文件。 当我重命名/移动监视目录时,我没有任何事件,WatchKey也不会失效。 我仍然从重命名的目录中获取事件,但是据我所知,除了WatchKey.watchable()之外,没有办法找到WatchKey的实际路径,但是它仍然返回原