我尝试查看某些文件的更改。
但是我从WatchKey
得到的watch_object.watch_service.poll(16, TimeUnit.MILLISECONDS);
始终是null
。
没有一个错误打印到控制台,所以我有点迷路。
public class FileWatcher implements Runnable {
public FileWatcher() {
}
static public class Watch_Object {
public File file;
public WatchService watch_service;
}
static public HashMap<Object, Watch_Object> watched_files = new HashMap<>();
static public boolean is_running = false;
static public synchronized void watch(Object obj, String filename) {
File file = new File(filename);
if (file.exists()) {
try {
WatchService watcher = null;
watcher = FileSystems.getDefault().newWatchService();
Watch_Object watch_object = new Watch_Object();
watch_object.file = file;
watch_object.watch_service = watcher;
watched_files.put(obj, watch_object);
Path path = file.toPath().getParent();
path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
if (!is_running) {
(new Thread(new FileWatcher())).start();
is_running = true;
}
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
}
else {
// Error
}
}
@Override
public void run() {
try {
while (true) {
synchronized(this) {
for (Watch_Object watch_object : watched_files.values()) {
WatchKey key = watch_object.watch_service.poll(16, TimeUnit.MILLISECONDS);
System.out.println("A");
if (key != null) {
System.out.println("B");
}
}
}
Thread.sleep(16);
}
}
catch (Throwable e) {
// Log or rethrow the error
e.printStackTrace();
}
}
}
要运行它:
public static void main(String[] args) {
// the obj is WIP, just use null for now
watch(null, "/Users/doekewartena/Desktop/test_image.png");
}
答案 0 :(得分:6)
我想在此前缀WatchService
高度依赖于实现:
平台依赖项
观察来自文件系统事件的实现旨在直接将其映射到本机文件事件通知功能(如果可用),或者在本机功能不可用时使用原始机制(例如轮询)。因此,有关如何检测事件,其及时性以及是否保留其顺序的许多细节都是高度特定于实现的。例如,当修改监视目录中的文件时,在某些实现中可能会导致单个
ENTRY_MODIFY
事件,而在其他实现中可能会导致多个事件。生存期很短的文件(意味着在创建后会很快删除的文件)可能无法被原始的实现检测到,这些实现会定期轮询文件系统以检测更改。如果监视的文件不在本地存储设备上,则可以检测到文件的更改是特定于实现的。特别是,不需要检测对在远程系统上执行的文件的更改。
您提到WatchService.poll
总是返回null
。这并不完全令人惊讶,因为poll()
和poll(long,TimeUnit)
都将返回null
,如果没有要处理的事件(类似于标准队列的行为)。但是您说即使您已经修改了监视的文件 * ,您仍然总是{em> null
。不幸的是,我无法使用OpenJDK 11.0.2(或JDK 1.8.0_202),Windows 10和本地存储设备重现该问题。
*问题注释中提到的内容是在清理之前。
尝试输入您的代码时,我发现B
已打印到控制台。当然,要想A
每隔16
毫秒打印一次WatchKey
太麻烦了,就不容易看到它了,但它确实在那里。但是,有一个问题是,在第一次修改事件之后,它将不再报告。这使我对您的代码有一些评论。
WatchKey.reset()
。处理完WatchKey
后,调用此方法很重要。该方法将WatchKey
标记为准备检测新事件。没有此呼叫,您将不会观察到后续事件。
reset()
中的poll the events。为了解决未看到的后续事件的问题,我天真地向B
添加了一个呼叫,而未做其他任何事情。这导致大量WatchKey.reset
被打印到控制台。我很困惑,因为我只修改了一次文件,但随后阅读了B
(强调我的)的文档:
重置此监视键。
如果此监视键已被取消或此监视键已处于就绪状态,则调用此方法无效。 否则,如果对象有待处理的事件,那么此监视键将立即重新排队到监视服务。如果没有待处理的事件,则监视键将处于就绪状态并将保持在直到检测到事件或取消监视键为止。
我看到的只是一次又一次的同一事件,因为我从未处理过它。向WatchEvent.pollEvents()
添加呼叫后,我不再被WatchService
所吸引。
WatchService
。您似乎想要一个可以监视任意数量的文件(并且仅监视那些文件)的类。这不需要每个文件WatchService
,因为您可以使用同一WatchService
注册多个目录。如果文件来自不同的FileSystem
,则要求使用多个WatchService
的情况。但是,您的代码始终使用default file system。
使用相同的poll
也无需使用poll
。我假设您当前使用WatchService
的原因是因为您需要检查每个import java.io.Closeable;
import java.io.IOException;
import java.nio.file.ClosedWatchServiceException;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
/**
* Watches files for modification events, but not for creation,
* deletion, or overflow events.
*/
public class FileWatcher implements Closeable, Runnable {
private final List<BiConsumer<? super FileWatcher, ? super Path>> handlers
= new CopyOnWriteArrayList<>();
private final Object lock = new Object();
private final Map<Path, Registry> registeredDirs = new HashMap<>();
private final Set<Path> watchedFiles = new HashSet<>();
private final AtomicBoolean running = new AtomicBoolean();
private final FileSystem fileSystem;
private final WatchService service;
public FileWatcher(FileSystem fs) throws IOException {
service = fs.newWatchService();
fileSystem = fs;
}
public FileSystem getFileSystem() {
return fileSystem;
}
public boolean startWatching(Path file) throws IOException {
Objects.requireNonNull(file);
synchronized (lock) {
if (watchedFiles.add(file)) {
Path directory = file.getParent();
if (registeredDirs.containsKey(directory)) {
registeredDirs.get(directory).incrementCount();
} else {
try {
WatchKey key = directory.register(service, ENTRY_MODIFY);
registeredDirs.put(directory, new Registry(key));
} catch (ClosedWatchServiceException | IllegalArgumentException
| IOException | SecurityException ex) {
watchedFiles.remove(file);
throw ex;
}
}
return true;
}
return false;
}
}
public boolean stopWatching(Path file) {
Objects.requireNonNull(file);
synchronized (lock) {
if (watchedFiles.remove(file)) {
Path directory = file.getParent();
Registry registry = registeredDirs.get(directory);
if (registry.decrementCount()) {
registeredDirs.remove(directory);
registry.cancelKey();
}
return true;
}
return false;
}
}
public void addHandler(BiConsumer<? super FileWatcher, ? super Path> handler) {
handlers.add(Objects.requireNonNull(handler));
}
public void removeHandler(BiConsumer<? super FileWatcher, ? super Path> handler) {
handlers.remove(Objects.requireNonNull(handler));
}
private void fireModifyEvent(Path source) {
for (BiConsumer<? super FileWatcher, ? super Path> handler : handlers) {
try {
handler.accept(this, source);
} catch (RuntimeException ex) {
Thread.currentThread().getUncaughtExceptionHandler()
.uncaughtException(Thread.currentThread(), ex);
}
}
}
@Override
public void close() throws IOException {
service.close();
synchronized (lock) {
registeredDirs.clear();
watchedFiles.clear();
}
}
@Override
public void run() {
if (running.compareAndSet(false, true)) {
try {
while (!Thread.interrupted()) {
WatchKey key = service.take();
for (WatchEvent<?> event : key.pollEvents()) {
Path source = ((Path) key.watchable())
.resolve((Path) event.context());
boolean isWatched;
synchronized (lock) {
isWatched = watchedFiles.contains(source);
}
if (isWatched) {
fireModifyEvent(source);
}
}
key.reset();
}
} catch (InterruptedException ignore) {
} finally {
running.set(false);
}
} else {
throw new IllegalStateException("already running");
}
}
private static class Registry {
private final WatchKey key;
private int count;
private Registry(WatchKey key) {
this.key = key;
incrementCount();
}
private void incrementCount() {
count++;
}
private boolean decrementCount() {
return --count <= 0;
}
private void cancelKey() {
key.cancel();
}
}
}
。由于现在只有一种,因此您可以使用阻塞WatchService.take()
方法。
我认为,这是一个小例子,可以满足您的需求。我不能保证它是完美的,因为还没有经过全面的测试。我也不能保证它将在您的计算机上运行。
FileWatcher
还有一个使用上述import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) throws IOException {
Path file = chooseFile();
if (file == null) {
return;
}
System.out.println("Entered \"" + file + "\"");
ExecutorService executor = Executors.newSingleThreadExecutor();
try (FileWatcher watcher = new FileWatcher(FileSystems.getDefault())) {
Future<?> task = executor.submit(watcher);
executor.shutdown();
watcher.addHandler((fw, path) -> System.out.println("File modified: " + path));
watcher.startWatching(file);
waitForExit();
task.cancel(true);
} finally {
executor.shutdownNow();
}
}
private static Path chooseFile() {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("Enter file (or 'exit' to exit application): ");
String line = scanner.nextLine();
if ("exit".equalsIgnoreCase(line.trim())) {
return null;
}
Path file = Paths.get(line).toAbsolutePath().normalize();
if (Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) {
return file;
}
System.out.println("File must exist and be a regular file. Try again.");
}
}
private static void waitForExit() {
System.out.println("\nType 'exit' to exit the application.");
Scanner scanner = new Scanner(System.in);
while (true) {
String line = scanner.nextLine();
if ("exit".equalsIgnoreCase(line.trim())) {
return;
}
}
}
}
的小型应用程序:
{{1}}
及其运行中的GIF:
答案 1 :(得分:0)
这是我正在使用的有效代码,它与您的代码非常相似。我希望这会有所帮助。
public void watchDirectory(Path dir) {
logger.info("Watching directory {} for new Files", dir);
WatchService watchService = FileSystems.getDefault().newWatchService();
registerRecursive(dir, watchService);
WatchKey key;
while ((key = watchService.take()) != null) {
key.pollEvents();
executorService.submit(this::performAction);
boolean reset = key.reset();
if (!reset) {
logger.error("Could not reset the WatchKey");
throw new RunTimeException("Could not reset the WatchKey");
}
}
}
private void performAction() {
// Your code after an event is registered
}
private void registerRecursive(Path root, WatchService watchService) throws IOException {
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);
return FileVisitResult.CONTINUE;
}
});
}