80

ディレクトリの代わりにファイルを登録しようとすると、java.nio.file.NotDirectoryExceptionスローされます。ディレクトリ全体ではなく、単一のファイルの変更をリッスンできますか?

4

7 に答える 7

107

ディレクトリに必要なファイルのイベントをフィルタリングするだけです。

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」であるかどうかを確認し、そうであれば何でもします。

于 2013-04-27T11:22:22.943 に答える
21

いいえ、ファイルを登録することはできません。監視サービスはこの方法では機能しません。ただし、ディレクトリを登録すると、実際には、ディレクトリ自体の変更ではなく、ディレクトリの子 (ファイルとサブディレクトリ) の変更が監視されます。

ファイルを監視する場合は、ファイルを含むディレクトリを監視サービスに登録します。Path.register() のドキュメントには次のように書かれています。

WatchKey java.nio.file.Path.register(WatchService ウォッチャー、Kind[] イベント、Modifier... modifiers) が IOException をスローします

このパスにあるファイルを監視サービスに登録します。

このリリースでは、このパスは存在するディレクトリを特定します。ディレクトリ内のエントリを監視できるように、ディレクトリは監視サービスに登録されます。

次に、エントリのイベントを処理し、イベントのコンテキスト値をチェックして、関心のあるファイルに関連するイベントを検出する必要があります。コンテキスト値は、エントリの名前を表します (実際には、親のパスに相対的なエントリのパスであり、これはまさに子の名前です)。ここにがあります。

于 2014-06-25T06:27:32.017 に答える
21

ディレクトリを監視し、特定のファイルをフィルタリングする必要があるという他の答えは正しいです。ただし、おそらくバックグラウンドでスレッドを実行する必要があります。受け入れられた回答は無期限にブロックされる可能性がwatchService.take();あり、WatchService を閉じません。別のスレッドに適したソリューションは次のようになります。

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()です。

于 2015-01-02T03:30:35.067 に答える
10

Apache は、メソッドを持つFileWatchDogクラスを提供しますdoOnChange

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 は、通知が即時であるため、より効率的です。

于 2014-02-17T09:57:24.267 に答える
8

個々のファイルを直接見ることはできませんが、必要のないものを除外できます。

これが私の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();
于 2016-10-12T16:24:35.490 に答える
5

他の人についてはわかりませんが、基本的な WatchService API を使用して 1 つのファイルの変更を監視するのに必要なコードの量に不満があります。それはもっと簡単でなければなりません!

サード パーティのライブラリを使用するいくつかの代替手段を次に示します。

于 2015-04-30T12:17:48.007 に答える
5

WatchServiceディレクトリと任意の数のグロブパターンを登録できるJava 1.7のラッパーを作成しました。このクラスはフィルタリングを処理し、関心のあるイベントのみを発行します。

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

完全なコードはこのリポジトリにあります。

于 2015-06-23T14:07:20.787 に答える