2013-04-27 11 views
44

Khi tôi đang cố gắng đăng ký một tập tin thay vì một thư mục java.nio.file.NotDirectoryException bị ném. Tôi có thể nghe một thay đổi tập tin duy nhất, không phải toàn bộ thư mục không?Tôi có thể xem thay đổi tệp đơn với WatchService (không phải toàn bộ thư mục) không?

+5

Javadoc: "Trong bản phát hành này, đường dẫn này tìm thư mục tồn tại. " ==> vì vậy câu trả lời là "không, bạn không thể đăng ký một tập tin". Sau đó: "Thư mục được đăng ký với dịch vụ đồng hồ sao cho các mục trong thư mục có thể được xem." ==> để đăng ký một thư mục thực sự theo dõi các sự kiện trên các mục nhập thư mục, không phải trên chính thư mục đó. Tên của các sự kiện nhắc nhở về những gì chúng liên quan đến, chúng bắt đầu bằng ENTRY_, như "ENTRY_MODIFY - mục nhập trong thư mục đã được sửa đổi". Câu trả lời đã chọn cung cấp chi tiết để sử dụng sự kiện. – mins

Trả lời

68

Chỉ cần lọc các sự kiện cho các tập tin mà bạn muốn trong thư mục:

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

Ở đây chúng ta kiểm tra xem các tập tin đã thay đổi là "myfile.txt", nếu nó là sau đó làm bất cứ điều gì.

+2

+1, tuyệt vời, cảm ơn! Có cách nào khác hiệu quả để xem một tệp không? – ADJ

+7

-1 Cung cấp giải pháp nhưng không thực sự trả lời câu hỏi. – lscoughlin

+0

Tôi đã gặp trình soạn thảo văn bản (krViewer) không sửa đổi tệp, nhưng tạo tệp tmp và trên Lưu chỉ thay thế tệp, vì vậy không có thông báo MODIFY nào được phát hành và tôi cũng phải tạo tài khoản. – mulya

3

Apache cung cấp lớp học FileWatchDog với phương thức doOnChange.

private class SomeWatchFile extends FileWatchdog { 

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

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

} 

Và cứ nơi nào bạn muốn, bạn có thể bắt đầu chủ đề này:

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

Các cuộc thăm dò lớp FileWatchDog của một tập tin lastModified() dấu thời gian. WatchService bản địa từ Java NIO hiệu quả hơn, vì các thông báo là ngay lập tức.

+7

Thật tuyệt khi biết thư viện lớp FileWatchdog đến từ đâu? – szydan

+1

từ log4j - org.apache.log4j.helpers – idog

14

Không thể đăng ký tệp, dịch vụ đồng hồ không hoạt động theo cách này. Nhưng đăng ký một thư mục thực sự đồng hồ thay đổi trên thư mục con (tệp và thư mục con), chứ không phải thay đổi trên thư mục.

Nếu bạn muốn xem tệp, khi đó bạn đăng ký thư mục chứa dịch vụ đồng hồ. Path.register() documentation nói:

WatchKey java.nio.file.Path.register (WatchService watcher, Kind [] sự kiện, bổ trợ ... bổ) throws IOException

Thanh ghi các tập tin nằm bằng con đường này với một dịch vụ đồng hồ.

Trong bản phát hành này, đường dẫn này tìm thư mục tồn tại. Thư mục được đăng ký với dịch vụ theo dõi để mục trong thư mục có thể được theo dõi

Sau đó, bạn cần phải xử lý các sự kiện trên mục, và phát hiện những liên quan đến các tập tin mà bạn quan tâm bằng cách kiểm tra giá trị ngữ cảnh của sự kiện. Giá trị ngữ cảnh đại diện cho tên của mục nhập (thực ra là đường dẫn của mục tương đối với đường dẫn của cha mẹ của nó, đó chính xác là tên con). Bạn có số example here.

10

Câu trả lời khác là bạn phải xem thư mục và bộ lọc cho tệp cụ thể của mình. Tuy nhiên, bạn có thể muốn một chuỗi chạy trong nền. Câu trả lời được chấp nhận có thể chặn vô thời hạn trên watchService.take(); và không đóng WatchService. Một giải pháp phù hợp cho một sợi riêng biệt có thể trông giống như:

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

Tôi đã cố gắng làm việc từ câu trả lời được chấp nhận và this article. Bạn có thể sử dụng chủ đề này với new FileWatcher(new File("/home/me/myfile")).start() và dừng lại bằng cách gọi stopThread() trên chuỗi.

2

Không chắc chắn về những người khác, nhưng tôi rên với số lượng mã cần thiết để xem một tệp duy nhất cho các thay đổi bằng API WatchService cơ bản. Nó phải đơn giản hơn!

Dưới đây là một vài lựa chọn thay thế sử dụng thư viện của bên thứ ba:

2

Tôi đã tạo một trình bao bọc xung quanh WatchService của Java 1.7 cho phép đăng ký một thư mục và bất kỳ số nào glob mẫu. Lớp này sẽ chăm sóc của lọc và chỉ phát ra sự kiện mà bạn đang quan tâm.

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

Toàn bộ code đang trong Gist này.

2

Bạn không thể xem trực tiếp từng tệp nhưng bạn có thể lọc ra những gì bạn không cần.

Đây là thực hiện FileWatcher lớp học của tôi:

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

Để sử dụng, bạn chỉ cần mở rộng và thực hiện các phương pháp onModified() như vậy:

import java.io.File; 

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

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

Cuối cùng, bắt đầu xem các file:

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