为什么WatchService会生成如此多的操作?

24
import java.io.*;
import java.nio.file.*;

public class Tmp {

    public static void main(String [] args) throws IOException {
        int count = 0;
        Path path = Paths.get("C:\\tmp\\");
        WatchService ws = null;
        try {
            ws = FileSystems.getDefault().newWatchService();
            path.register(ws, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
                    StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.OVERFLOW);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

        while(true) {
            WatchKey key = null;
            try {
                key = ws.take();
            } catch(InterruptedException ie) {
                ie.printStackTrace();
            }

            for(WatchEvent<?> event: key.pollEvents()) {
                switch(event.kind().name()) {
                    case "OVERFLOW":
                        System.out.println(++count + ": OVERFLOW");
                        break;
                    case "ENTRY_MODIFY":
                        System.out.println(++count + ": File " + event.context() + " is changed!");
                        break;
                    case "ENTRY_CREATE":
                        System.out.println(++count + ": File " + event.context() + " is created!");
                        break;
                    case "ENTRY_DELETE":
                        System.out.println(++count + ": File " + event.context() + " is deleted!");
                        break;
                    default:
                        System.out.println(++count + ": UNKNOWN EVENT!");
                }
            }

            key.reset();
        }    
    }
}

当我运行这个程序,然后打开Notepad++,创建一个新的空文件并将其保存为a.txtC:\ tmp \ 目录中,我得到了以下输出:

1: File a.txt is created!
2: File a.txt is deleted!
3: File a.txt is created!
为什么会这样?看起来这个文件是被创建、删除,然后再次创建了。为什么呢?
当我往文件中输入一些文本并保存时,输出结果是:
4: File a.txt is changed!
5: File a.txt is changed!

为什么它改变了两次?


5
我认为你在使用WatchService时看到的行为是由于Notepad++以及Windows操作系统执行IO操作的方式造成的。我发现像“标准”的Windows记事本通常会产生最符合预期的行为。我猜测,如果你使用进程资源管理器(http://technet.microsoft.com/en-gb/sysinternals/bb896653.aspx)来监视操作系统级别的IO活动,你会看到相同的结果。 - Paul H
2
这可能是因为内容和元数据的写入是分开进行的原因。 - Tadas S
4个回答

2

Watch Service的修改事件会生成两个事件。当我们修改一个已经存在的文件时,文件系统会首先创建一个大小为0字节的文件并触发一个修改事件,然后再在该文件上写入数据。接着,它再次触发一个修改事件。这就是为什么会出现两个修改事件。因此,为了解决这个问题,我只需使用计数器来检查我的任务是否应该在偶数次上触发一次。

        Path path = null;
        int count = 0;

        try {
            path = Paths.get(new Utility().getConfDirPath());
            System.out.println("Path: " + path);
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }

        WatchService watchService = null;
        try {
            watchService = FileSystems.getDefault().newWatchService();
            path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY,StandardWatchEventKinds.ENTRY_DELETE);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }


        while(true) {
            WatchKey key = null;
            try {
                key = watchService.take();
            } catch(InterruptedException ie) {
                ie.printStackTrace();
            }

            for(WatchEvent<?> event: key.pollEvents()) {
                switch(event.kind().name()) {
                    case "ENTRY_MODIFY":
                        System.out.println(++count + ": File " + event.context() + " is changed!");

                        if (count%2==0) {
                            doOnChange(); // do whatever you want
                        }
                        break;
                    case "ENTRY_DELETE":
                        System.out.println(++count + ": File " + event.context() + " is deleted!");
                        break;
                    default:
                        System.out.println(++count + ": UNKNOWN EVENT!");
                }
            }

            // reset the key
            boolean valid = key.reset();
            if (!valid) {
                System.out.println("Key has been unregistered");
            }

        }    

0

这对我有效

    // get the first event before looping
    WatchKey key = this.watcher.take();

    // reset key (executed twice but not invoke the polled events)
    while (key != null && key.reset() ) { 
      // polled events
      for (final WatchEvent<?> event : key.pollEvents()) {
        System.out.printf("\nGlobal received: %s, event for file: %s\n", event.kind(),
            event.context());

        switch (event.kind().name()) {
        case "ENTRY_CREATE":
          LOGGER.debug("event ENTRY_CREATE");
          break;
        case "ENTRY_DELETE":
          LOGGER.debug("event ENTRY_DELETE");
          break;
        case "ENTRY_MODIFY":
          LOGGER.debug("event ENTRY_MODIFY");
          break;
        default:
          LOGGER.debug("event other [OVERFLOW]");
          break;
        }
      }

      key = this.watcher.take();
    }

0

在我的系统中(Window 7 + 1.7.0_21),文件创建和删除事件正常工作。

对于每个对该文件进行的Ctrl+s操作,更改事件消息会显示n次。

      // Removed the "//" after director name as D://tmp"
      //Added just to see the message with a 1 ms gap.
      Thread.sleep(1000); // after key.reset();

例子: 如果我们打开文件并一直按下crtl + s(保存没有任何更改/有更改)。每次保存操作都会显示以下消息(重复)。

     File YourFileName.txt is changed!

原因是在Windows上,WatchService使用时间戳而非校验和来比较文件更改。

这里提供了更多描述 平台依赖性


这不是1毫秒的间隔,而是1秒的间隔。当然,在计算机术语中等待很长时间再次检查时,您将看不到效果。至于这个:“原因是在Windows中,WatchService使用时间戳而不是校验和来比较文件更改。” 嗯...什么? - Robin Green
@RobinGreen,请告诉我你的异常和答案是什么。 - VKPRO

0

我创建了一个小的FileWatcher实用程序库: https://github.com/finsterwalder/fileutils

它允许设置一个宽限期。在宽限期内的多个事件会被累积,只有一次触发。

你不应该使用Notepad++进行实验,因为你不知道Notepad++在做什么。它可能会多次写入文件,或者在完成后将文件重命名为不同的名称等。 编写自己的代码来操作文件并监视它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接