it-swarm.com.de

Kann ich mit WatchService auf einzelne Dateiänderungen achten (nicht auf das gesamte Verzeichnis)?

Wenn ich versuche, eine Datei anstelle eines Verzeichnisses zu registrieren, Java.nio.file.NotDirectoryException ist geworfen. Kann ich auf eine einzelne Dateiänderung warten, nicht auf das gesamte Verzeichnis?

62
fedor.belov

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 geht, was auch immer.

87

Nein, es ist nicht möglich, eine Datei zu registrieren. Der Überwachungsdienst funktioniert nicht auf diese Weise. Aber das Registrieren eines Verzeichnisses überwacht tatsächlich Änderungen an den untergeordneten Verzeichnissen (den Dateien) und Unterverzeichnisse), nicht die Änderungen am Verzeichnis selbst.

Wenn Sie eine Datei überwachen möchten, registrieren Sie das übergeordnete Verzeichnis beim Überwachungsdienst. Path.register () Dokumentation sagt:

WatchKey Java.nio.file.Path.register (WatchService-Beobachter, Kind [] -Ereignisse, Modifizierer ... -Modifizierer) löst IOException aus

Registriert die über diesen Pfad gefundene Datei bei einem Überwachungsdienst.

In dieser Version sucht dieser Pfad ein vorhandenes Verzeichnis. Das Verzeichnis ist registriert beim Überwachungsdienst damit Einträge im Verzeichnis kann angesehen werden

Anschließend müssen Sie Ereignisse für Einträge verarbeiten und diejenigen ermitteln, die sich auf die gewünschte Datei beziehen, indem Sie den Kontextwert des Ereignisses überprüfen. Der Kontextwert stellt den Namen des Eintrags dar (tatsächlich der Pfad des Eintrags relativ zum Pfad seines übergeordneten Elements, der genau der untergeordnete Name ist). Sie haben ein Beispiel hier .

20
mins

Andere Antworten sind richtig, dass Sie ein Verzeichnis überwachen und nach Ihrer bestimmten Datei filtern müssen. Wahrscheinlich möchten Sie jedoch, dass ein Thread im Hintergrund ausgeführt wird. Die akzeptierte Antwort kann für watchService.take(); auf unbestimmte Zeit blockiert werden 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 zu arbeiten und dieser Artikel . Sie sollten in der Lage sein, diesen Thread mit new FileWatcher(new File("/home/me/myfile")).start() zu verwenden und ihn zu stoppen, indem Sie stopThread() für den Thread aufrufen.

16
timrs2998

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 ruft den lastModified()-Zeitstempel einer Datei ab. Der native WatchService von Java NIO ist effizienter, da Benachrichtigungen sofort erfolgen.

10
idog

Sie können eine einzelne Datei nicht direkt anzeigen, aber Sie können herausfiltern, was Sie nicht benötigen.

Hier ist meine Implementierung der Klasse 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();
}

Um dies zu nutzen, müssen Sie nur die onModified() -Methode wie folgt 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!");
    }
}

Schauen Sie sich zum Schluss die Datei an:

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

Ich habe einen Wrapper um Java 1.7's WatchService erstellt, 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);
}

Kompletter Code ist in diesem repo .

5
Hindol

Ich bin mir anderer nicht sicher, aber ich ächze über die Menge an Code, die zum Überwachen einer einzelnen Datei auf Änderungen mithilfe der grundlegenden WatchService-API erforderlich ist. Einfacher muss es sein!

Hier sind einige Alternativen, die Bibliotheken von Drittanbietern verwenden:

3
John Rix