在Java中,监视所追加文件的最佳/最安全的模式是什么?

2022-09-04 01:40:06

其他人的过程是在事件发生时,通过一次向该文件追加一行来创建 CSV 文件。我无法控制文件格式或其他进程,但我知道它只会附加。

在Java程序中,我想监视此文件,当附加一行时,读取新行并根据内容做出反应。暂时忽略 CSV 解析问题。监视文件是否有更改并一次读取一行的最佳方法是什么?

理想情况下,这将使用标准库类。该文件很可能位于网络驱动器上,因此我想要一些能够正常运行的功能。如果可能的话,我宁愿不使用轮询 - 我更喜欢某种阻止解决方案。

编辑 - 鉴于标准类无法实现阻塞解决方案(感谢您的答案),什么是最强大的轮询解决方案?我宁愿每次都不要重新读取整个文件,因为它可能会变得非常大。


答案 1

从Java 7开始,文件系统类上就有了newWatchService()方法。

但是,有一些警告:

  • 它只是Java 7
  • 这是一种可选方法
  • 它只监视目录,因此您必须自己处理文件,并担心文件移动等

在Java 7之前,标准API是不可能的。

我尝试了以下方法(以1秒的间隔进行轮询),并且它有效(只是在处理中打印):

  private static void monitorFile(File file) throws IOException {
    final int POLL_INTERVAL = 1000;
    FileReader reader = new FileReader(file);
    BufferedReader buffered = new BufferedReader(reader);
    try {
      while(true) {
        String line = buffered.readLine();
        if(line == null) {
          // end of file, start polling
          Thread.sleep(POLL_INTERVAL);
        } else {
          System.out.println(line);
        }
      }
    } catch(InterruptedException ex) {
     ex.printStackTrace();
    }
  }

由于没有其他人建议使用当前生产Java的解决方案,我想我会添加它。如果有缺陷,请在评论中添加。


答案 2

您可以使用 WatchService 类进行注册,以便在文件发生任何更改时收到文件系统的通知。这需要Java7,此处的文档链接 http://docs.oracle.com/javase/tutorial/essential/io/notification.html

这里是代码片段代码来做到这一点:

public FileWatcher(Path dir) {
   this.watcher = FileSystems.getDefault().newWatchService();
   WatchKey key = dir.register(watcher, ENTRY_MODIFY);
}

void processEvents() {
    for (;;) {
        // wait for key to be signalled
        WatchKey key;
        try {
            key = watcher.take();
        } catch (InterruptedException x) {
            return;
        }

        for (WatchEvent<?> event : key.pollEvents()) {
            WatchEvent.Kind<?> kind = event.kind();

            if (kind == OVERFLOW) {
                continue;
            }
            // Context for directory entry event is the file name of entry
            WatchEvent<Path> ev = cast(event);
            Path name = ev.context();
            Path child = dir.resolve(name);
            // print out event
            System.out.format("%s: %s file \n", event.kind().name(), child);
        }
        // reset key and remove from set if directory no longer accessible
        boolean valid = key.reset();
    }
}