Почему WatchService генерирует так много операций?
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.txt
в каталоге C:\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!
Почему это изменилось дважды?
Ответы
Ответ 1
Событие Watch Service Modify генерирует два события. Когда мы модифицируем уже существующий файл, файловая система сначала создает его с 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");
}
}
Ответ 2
События создания и удаления файлов корректно работают в моей системе (Window 7 + 1.7.0_21).
Сообщение о событии изменения отображает количество времени (n) для каждой операции Ctrl + s в этом файле.
// 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 сравнивает изменения файлов с меткой времени вместо контрольной суммы.
Подробное описание приведено здесь зависимости от платформы
Ответ 3
это работает для меня
// 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();
}
Ответ 4
Я создал небольшую библиотеку утилиты FileWatcher:
https://github.com/finsterwalder/fileutils
Позволяет установить льготный период. Несколько событий внутри льготного периода накапливаются и запускаются только один раз.
Вы не должны использовать Notepad ++ для своих экспериментов, так как вы не знаете, что делает Notepad ++. Возможно, Notepad ++ на самом деле пишет файл несколько раз. Или он может написать файл с другим именем и переименовать его, когда это будет сделано или что-то еще.
Создайте свой собственный код для управления файлами и посмотрите, что.