ディレクトリの代わりにファイルを登録しようとしているときJava.nio.file.NotDirectoryException
がスローされます。ディレクトリ全体ではなく、単一のファイル変更をリッスンできますか?
ディレクトリで必要なファイルのイベントをフィルタリングするだけです:
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」であるかどうかを確認します。
いいえ、ファイルを登録することはできません。監視サービスはこの方法では機能しません。しかし、登録ディレクトリを実際にwatchesディレクトリの子(ファイルおよびサブディレクトリ)、ディレクトリ自体の変更ではありません。
ファイルを監視する場合は、監視サービスに格納ディレクトリを登録します。 Path.register()documentation 言う:
WatchKey Java.nio.file.Path.register(WatchService watcher、Kind [] events、Modifier ... modifiers)throws IOException
このパスにあるファイルを監視サービスに登録します。
このリリースでは、このパスは存在するディレクトリを見つけます。 ディレクトリはregistered監視サービスにそのためディレクトリ内のエントリ視聴可能
次に、エントリのイベントを処理し、イベントのコンテキスト値を確認して、関心のあるファイルに関連するイベントを検出する必要があります。コンテキスト値は、エントリの名前を表します(実際には、その親のパスに対するエントリのパス。これは正確に子の名前です)。 ここの例 があります。
他の答えは正しいので、特定のファイルのディレクトリとフィルターを監視する必要があります。ただし、おそらくバックグラウンドでスレッドを実行する必要があります。受け入れられた回答は、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()
を呼び出して停止できる必要があります。
Apacheは、doOnChange
メソッドを持つ FileWatchDog クラスを提供します。
_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は、通知が即時であるため、より効率的です。
個々のファイルを直接見ることはできませんが、不要なものを除外することはできます。
これが私の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();
_
ディレクトリと任意の数のglobパターンを登録できるJava 1.7のWatchService
の周りにラッパーを作成しました。クラスはフィルタリングを処理し、関心のあるイベントのみを発行します。
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);
}
完全なコードはこの repo にあります。
他の人についてはわかりませんが、基本的なWatchService APIを使用して、単一ファイルの変更を監視するために必要なコードの量にうめきます。もっとシンプルにする必要があります!
サードパーティのライブラリを使用するいくつかの選択肢があります。