我可以使用WatchService监视单个文件的更改吗(不是整个目录)?
当我试图注册一个文件,而不是一个目录java.nio.file.NotDirectoryException
抛出。 我可以听单个文件更改,而不是整个目录?
只需在目录中筛选所需文件的事件即可:
final Path path = FileSystems.getDefault().getPath(System.getProperty("user.home"), "Desktop"); System.out.println(path); try (final WatchService watchService = FileSystems.getDefault().newWatchService()) { final WatchKey watchKey = path.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY); while (true) { final WatchKey wk = watchService.take(); for (WatchEvent<?> event : wk.pollEvents()) { //we only register "ENTRY_MODIFY" so the context is always a Path. final Path changed = (Path) event.context(); System.out.println(changed); if (changed.endsWith("myFile.txt")) { System.out.println("My file has changed"); } } // reset the key boolean valid = wk.reset(); if (!valid) { System.out.println("Key has been unregisterede"); } } }
在这里,我们检查一下被更改的文件是否是“myFile.txt”,如果是的话就做任何事情。
不可以注册文件,手表服务不能这样工作。 但是注册一个目录实际上是监视子目录(文件和子目录)的变化,而不是目录本身的变化。
如果你想看一个文件,那么你注册包含目录与监视服务。 Path.register()文档说:
WatchKey java.nio.file.Path.register(WatchService watcher,Kind [] events,Modifier … modifiers)throws IOException
使用监视服务注册位于此path的文件。
在此版本中,此path查找存在的目录。 该目录已经注册到监视服务, 以便可以监视目录中的条目
然后,您需要处理条目上的事件,并通过检查事件的上下文值来检测与您感兴趣的文件相关的事件。 上下文值表示条目的名称(实际上是条目相对于其父代path的path,这正是子名称)。 你在这里有一个例子 。
其他答案是正确的,你必须看一个目录,并筛选你的特定文件。 但是,您可能需要在后台运行一个线程。 被接受的答案可以无限地阻止watchService.take();
并不closuresWatchService。 适用于单独线程的解决scheme可能如下所示:
public class FileWatcher extends Thread { private final File file; private AtomicBoolean stop = new AtomicBoolean(false); public FileWatcher(File file) { this.file = file; } public boolean isStopped() { return stop.get(); } public void stopThread() { stop.set(true); } public void doOnChange() { // Do whatever action you want here } @Override public void run() { try (WatchService watcher = FileSystems.getDefault().newWatchService()) { Path path = file.toPath().getParent(); path.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY); while (!isStopped()) { WatchKey key; try { key = watcher.poll(25, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { return; } if (key == null) { Thread.yield(); continue; } for (WatchEvent<?> event : key.pollEvents()) { WatchEvent.Kind<?> kind = event.kind(); @SuppressWarnings("unchecked") WatchEvent<Path> ev = (WatchEvent<Path>) event; Path filename = ev.context(); if (kind == StandardWatchEventKinds.OVERFLOW) { Thread.yield(); continue; } else if (kind == java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY && filename.toString().equals(file.getName())) { doOnChange(); } boolean valid = key.reset(); if (!valid) { break; } } Thread.yield(); } } catch (Throwable e) { // Log or rethrow the error } } }
我尝试从接受的答案和这篇文章工作 。 你应该可以在new FileWatcher(new File("/home/me/myfile")).start()
使用这个线程,并通过调用线程上的stopThread()
来停止它。
Apache提供了一个带有doOnChange
方法的FileWatchDog类。
private class SomeWatchFile extends FileWatchdog { protected SomeWatchFile(String filename) { super(filename); } @Override protected void doOnChange() { fileChanged= true; } }
而且你想要的地方你可以开始这个线程:
SomeWatchFile someWatchFile = new SomeWatchFile (path); someWatchFile.start();
FileWatchDog类轮询文件的lastModified()
时间戳。 来自Java NIO的原生WatchService效率更高,因为通知是即时的。
对其他人不太确定,但是我呻吟着使用基本的WatchService API来观察单个文件所需的代码量。 它必须更简单!
这里有一些使用第三方库的替代scheme:
- 使用Apache Commonsconfiguration
- 使用Spring框架中的spring-loaded包 (没有find这个副本的示例实现,但它看起来很直接使用)
我已经创build了Java 1.7的WatchService
包装,允许注册一个目录和任何数量的glob模式。 这个class将照顾过滤,只发出你感兴趣的事件。
try { DirectoryWatchService watchService = new SimpleDirectoryWatchService(); // May throw watchService.register( // May throw new DirectoryWatchService.OnFileChangeListener() { @Override public void onFileCreate(String filePath) { // File created } @Override public void onFileModify(String filePath) { // File modified } @Override public void onFileDelete(String filePath) { // File deleted } }, <directory>, // Directory to watch <file-glob-pattern-1>, // Eg "*.log" <file-glob-pattern-2>, // Eg "input-?.txt" <file-glob-pattern-3>, // Eg "config.ini" ... // As many patterns as you like ); watchService.start(); // The actual watcher runs on a new thread } catch (IOException e) { LOGGER.error("Unable to register file change listener for " + fileName); }
完整的代码在这个Gist 。
您无法直接观看单个文件,但可以过滤掉不需要的文件。
这是我的FileWatcher
类的实现:
import java.io.File; import java.nio.file.*; import java.nio.file.WatchEvent.Kind; import static java.nio.file.StandardWatchEventKinds.*; public abstract class FileWatcher { private Path folderPath; private String watchFile; public FileWatcher(String watchFile) { Path filePath = Paths.get(watchFile); boolean isRegularFile = Files.isRegularFile(filePath); if (!isRegularFile) { // Do not allow this to be a folder since we want to watch files throw new IllegalArgumentException(watchFile + " is not a regular file"); } // This is always a folder folderPath = filePath.getParent(); // Keep this relative to the watched folder this.watchFile = watchFile.replace(folderPath.toString() + File.separator, ""); } public void watchFile() throws Exception { // We obtain the file system of the Path FileSystem fileSystem = folderPath.getFileSystem(); // We create the new WatchService using the try-with-resources block try (WatchService service = fileSystem.newWatchService()) { // We watch for modification events folderPath.register(service, ENTRY_MODIFY); // Start the infinite polling loop while (true) { // Wait for the next event WatchKey watchKey = service.take(); for (WatchEvent<?> watchEvent : watchKey.pollEvents()) { // Get the type of the event Kind<?> kind = watchEvent.kind(); if (kind == ENTRY_MODIFY) { Path watchEventPath = (Path) watchEvent.context(); // Call this if the right file is involved if (watchEventPath.toString().equals(watchFile)) { onModified(); } } } if (!watchKey.reset()) { // Exit if no longer valid break; } } } } public abstract void onModified(); }
要使用这个,你只需要像这样扩展和实现onModified()
方法:
import java.io.File; public class MyFileWatcher extends FileWatcher { public MyFileWatcher(String watchFile) { super(watchFile); } @Override public void onModified() { System.out.println("Modified!"); } }
最后,开始观看文件:
String watchFile = System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "Test.txt"; FileWatcher fileWatcher = new MyFileWatcher(watchFile); fileWatcher.watchFile();