web-dev-qa-db-fra.com

Puis-je surveiller le changement de fichier unique avec WatchService (pas tout le répertoire)?

Quand j'essaie d'enregistrer un fichier au lieu d'un répertoire Java.nio.file.NotDirectoryException Est lancé. Puis-je écouter un seul changement de fichier, pas tout le répertoire?

62
fedor.belov

Il suffit de filtrer les événements pour le fichier que vous voulez dans le répertoire:

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

Ici, nous vérifions si le fichier modifié est "myFile.txt", s'il fait quoi que ce soit.

87
Boris the Spider

Non, il n'est pas possible d'enregistrer un fichier, le service de surveillance ne fonctionne pas de cette façon. Mais l'enregistrement d'un répertoire surveille les modifications apportées au répertoire enfants (les fichiers et sous-répertoires), pas les modifications apportées au répertoire lui-même.

Si vous souhaitez regarder un fichier, vous enregistrez le répertoire contenant auprès du service de surveillance. documentation de Path.register () dit:

WatchKey Java.nio.file.Path.register (observateur WatchService, événements Kind [], modificateurs ...) lève une exception IOException

Enregistre le fichier situé par ce chemin avec un service de surveillance.

Dans cette version, ce chemin localise un répertoire existant. Le répertoire est enregistré avec le service de surveillance afin que les entrées du répertoire peut être regardé

Ensuite, vous devez traiter les événements sur les entrées et détecter ceux liés au fichier qui vous intéresse en vérifiant la valeur de contexte de l'événement. La valeur de contexte représente le nom de l'entrée (en fait, le chemin de l'entrée par rapport au chemin de son parent, qui est exactement le nom de l'enfant). Vous avez un exemple ici .

20
mins

D'autres réponses sont justes que vous devez regarder un répertoire et filtrer pour votre fichier particulier. Cependant, vous voulez probablement qu'un fil s'exécute en arrière-plan. La réponse acceptée peut bloquer indéfiniment sur watchService.take(); et ne ferme pas WatchService. Une solution appropriée pour un thread séparé pourrait ressembler à ceci:

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

J'ai essayé de travailler à partir de la réponse acceptée et cet article . Vous devriez pouvoir utiliser ce fil avec new FileWatcher(new File("/home/me/myfile")).start() et l'arrêter en appelant stopThread() sur le fil.

16
timrs2998

Apache propose une classe FileWatchDog avec une méthode doOnChange.

private class SomeWatchFile extends FileWatchdog {

    protected SomeWatchFile(String filename) {
        super(filename);
    }

    @Override
    protected void doOnChange() {
        fileChanged= true;
    }

}

Et où vous voulez, vous pouvez commencer ce fil de discussion:

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

La classe FileWatchDog interroge l'horodatage lastModified() d'un fichier. Le WatchService natif de Java NIO est plus efficace, car les notifications sont immédiates.

10
idog

Vous ne pouvez pas regarder directement un fichier individuel, mais vous pouvez filtrer ce dont vous n’avez pas besoin.

Voici mon implémentation de classe 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();
}

Pour l'utiliser, il vous suffit d'étendre et d'implémenter la méthode onModified() comme ceci:

import Java.io.File;

public class MyFileWatcher extends FileWatcher
{
    public MyFileWatcher(String watchFile)
    {
        super(watchFile);
    }

    @Override
    public void onModified()
    {
        System.out.println("Modified!");
    }
}

Enfin, commencez à regarder le fichier:

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

J'ai créé un wrapper autour de Java 1.7's WatchService) _ qui permet d'enregistrer un répertoire et un nombre quelconque de glob Cette classe se chargera du filtrage et n’émettra que les événements qui vous intéressent.

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

Le code complet est dans ce repo .

5
Hindol

Pas sûr des autres, mais je gémis devant la quantité de code nécessaire pour surveiller les modifications d'un fichier à l'aide de l'API WatchService de base. Ça doit être plus simple!

Voici quelques alternatives utilisant des bibliothèques tierces:

3
John Rix