Wenn ich versuche, eine Datei anstelle eines Verzeichnisses zu registrieren, java.nio.file.NotDirectoryException
wird ausgelöst. Kann ich auf eine einzelne Dateiänderung warten, nicht auf das gesamte Verzeichnis?
Wenn ich versuche, eine Datei anstelle eines Verzeichnisses zu registrieren, java.nio.file.NotDirectoryException
wird ausgelöst. Kann ich auf eine einzelne Dateiänderung warten, nicht auf das gesamte Verzeichnis?
Antworten:
Filtern Sie einfach die Ereignisse für die gewünschte Datei im Verzeichnis:
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");
}
}
}
Hier prüfen wir, ob die geänderte Datei "myFile.txt" ist, wenn es dann was auch immer ist.
OVERFLOW
. Sie müssen sich dafür nicht registrieren. Beispiel hier .
Nein, es ist nicht möglich, eine Datei zu registrieren. Der Überwachungsdienst funktioniert auf diese Weise nicht. Beim Registrieren eines Verzeichnisses werden jedoch Änderungen an den untergeordneten Verzeichnissen (den Dateien und Unterverzeichnissen) und nicht an den Änderungen am Verzeichnis selbst überwacht.
Wenn Sie eine Datei ansehen möchten, registrieren Sie das enthaltene Verzeichnis beim Überwachungsdienst. Die Dokumentation zu Path.register () lautet:
WatchKey java.nio.file.Path.register (WatchService-Beobachter, Kind [] -Ereignisse, Modifikator ... Modifikatoren) löst eine IOException aus
Registriert die unter diesem Pfad befindliche Datei bei einem Überwachungsdienst.
In dieser Version sucht dieser Pfad ein vorhandenes Verzeichnis. Das Verzeichnis ist beim Überwachungsdienst registriert , damit Einträge im Verzeichnis überwacht werden können
Anschließend müssen Sie Ereignisse für Einträge verarbeiten und diejenigen identifizieren, die sich auf die Datei beziehen, an der Sie interessiert sind, indem Sie den Kontextwert des Ereignisses überprüfen. Der Kontextwert stellt den Namen des Eintrags dar (tatsächlich den Pfad des Eintrags relativ zum Pfad seines übergeordneten Eintrags, der genau der untergeordnete Name ist). Sie haben hier ein Beispiel .
Andere Antworten sind richtig, dass Sie ein Verzeichnis überwachen und nach Ihrer bestimmten Datei filtern müssen. Sie möchten jedoch wahrscheinlich, dass ein Thread im Hintergrund ausgeführt wird. Die akzeptierte Antwort kann auf unbestimmte Zeit blockiert werden watchService.take();
und schließt den WatchService nicht. Eine Lösung, die für einen separaten Thread geeignet ist, könnte folgendermaßen aussehen:
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
}
}
}
Ich habe versucht, anhand der akzeptierten Antwort und dieses Artikels zu arbeiten . Sie sollten in der Lage sein, diesen Thread mit zu verwenden new FileWatcher(new File("/home/me/myfile")).start()
und ihn zu stoppen, indem Sie stopThread()
den Thread aufrufen .
setDaemon(boolean)
vorher auf, run()
je nachdem, wie sich Ihre App verhalten soll.
Apache bietet eine FileWatchDog- Klasse mit einer doOnChange
Methode an.
private class SomeWatchFile extends FileWatchdog {
protected SomeWatchFile(String filename) {
super(filename);
}
@Override
protected void doOnChange() {
fileChanged= true;
}
}
Und wo immer Sie wollen, können Sie diesen Thread starten:
SomeWatchFile someWatchFile = new SomeWatchFile (path);
someWatchFile.start();
Die FileWatchDog-Klasse fragt den lastModified()
Zeitstempel einer Datei ab . Der native WatchService von Java NIO ist effizienter, da Benachrichtigungen sofort erfolgen.
Sie können eine einzelne Datei nicht direkt ansehen, aber Sie können herausfiltern, was Sie nicht benötigen.
Hier ist meine FileWatcher
Klassenimplementierung:
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();
}
Um dies zu verwenden, müssen Sie die onModified()
Methode nur folgendermaßen erweitern und implementieren :
import java.io.File;
public class MyFileWatcher extends FileWatcher
{
public MyFileWatcher(String watchFile)
{
super(watchFile);
}
@Override
public void onModified()
{
System.out.println("Modified!");
}
}
Beginnen Sie schließlich mit dem Betrachten der Datei:
String watchFile = System.getProperty("user.home") + File.separator + "Desktop" + File.separator + "Test.txt";
FileWatcher fileWatcher = new MyFileWatcher(watchFile);
fileWatcher.watchFile();
Ich bin mir bei anderen nicht sicher, aber ich stöhne über die Menge an Code, die erforderlich ist, um eine einzelne Datei mithilfe der grundlegenden WatchService-API auf Änderungen zu überwachen. Es muss einfacher sein!
Hier sind einige Alternativen, die Bibliotheken von Drittanbietern verwenden:
Ich habe einen Wrapper um Java 1.7 erstellt WatchService
, mit dem ein Verzeichnis und eine beliebige Anzahl von Glob- Mustern registriert werden können. Diese Klasse kümmert sich um die Filterung und gibt nur Ereignisse aus, an denen Sie interessiert sind.
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);
}
Der vollständige Code befindet sich in diesem Repo .