java watchservice

java - ¿Puedo ver el cambio de un solo archivo con WatchService(no el directorio completo)?



(7)

Apache ofrece una clase FileWatchDog con un método doOnChange .

private class SomeWatchFile extends FileWatchdog { protected SomeWatchFile(String filename) { super(filename); } @Override protected void doOnChange() { fileChanged= true; } }

Y donde quieras, puedes comenzar este hilo:

SomeWatchFile someWatchFile = new SomeWatchFile (path); someWatchFile.start();

La clase FileWatchDog sondea la lastModified() marca de tiempo de un archivo lastModified() . El WatchService nativo de Java NIO es más eficiente, ya que las notificaciones son inmediatas.

Cuando intento registrar un archivo en lugar de un directorio, se lanza java.nio.file.NotDirectoryException . ¿Puedo escuchar un solo cambio de archivo, no todo el directorio?


He creado un envoltorio alrededor del WatchService Java 1.7 que permite registrar un directorio y cualquier cantidad de patrones glob . Esta clase se encargará del filtrado y solo emitirá los eventos que te interesen.

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>, // E.g. "*.log" <file-glob-pattern-2>, // E.g. "input-?.txt" <file-glob-pattern-3>, // E.g. "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); }

El código completo está en este repo .



No puede ver un archivo individual directamente, pero puede filtrar lo que no necesita.

Aquí está mi implementación de la clase 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(); }

Para usar esto, solo tienes que extender e implementar el método onModified() manera:

import java.io.File; public class MyFileWatcher extends FileWatcher { public MyFileWatcher(String watchFile) { super(watchFile); } @Override public void onModified() { System.out.println("Modified!"); } }

Finalmente, comienza a mirar el archivo:

String watchFile = System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "Test.txt"; FileWatcher fileWatcher = new MyFileWatcher(watchFile); fileWatcher.watchFile();


No, no es posible registrar un archivo, el servicio de vigilancia no funciona de esta manera. Pero el registro de un directorio realmente observa los cambios en el directorio hijos (los archivos y subdirectorios), no los cambios en el directorio en sí.

Si desea ver un archivo, entonces registra el directorio que lo contiene con el servicio de vigilancia. La documentación de Path.register () dice:

WatchKey java.nio.file.Path.register (watchService watcher, Kind [] events, Modifier ... modificadores) lanza IOException

Registra el archivo ubicado en esta ruta con un servicio de vigilancia.

En esta versión, esta ruta localiza un directorio que existe. El directorio está registrado con el servicio de vigilancia para poder ver las entradas en el directorio

Luego debe procesar los eventos en las entradas y detectar los relacionados con el archivo que le interesa, al verificar el valor del contexto del evento. El valor de contexto representa el nombre de la entrada (en realidad, la ruta de la entrada relativa a la ruta de acceso de su elemento principal, que es exactamente el nombre del elemento secundario). Usted tiene un ejemplo aquí .


Otras respuestas son correctas, debes mirar un directorio y filtrar tu archivo en particular. Sin embargo, es probable que desee un subproceso ejecutándose en segundo plano. La respuesta aceptada puede bloquearse indefinidamente en watchService.take(); y no cierra el WatchService. Una solución adecuada para un hilo separado podría verse así:

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

Traté de trabajar desde la respuesta aceptada y este artículo . Debería poder usar este hilo con el new FileWatcher(new File("/home/me/myfile")).start() y detenerlo al llamar a stopThread() en el hilo.


Simplemente filtra los eventos para el archivo que deseas en el directorio:

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

Aquí verificamos si el archivo modificado es "myFile.txt", si es entonces haga lo que sea.