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

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

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

理想情况下,这将使用标准库类。 该文件可能在网络驱动器上,所以我想要一些强大的失败。 如果可能的话我宁愿不使用轮询 – 我更喜欢某种阻塞解决方案。

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

从Java 7开始, FileSystem类上就有了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的解决方案我以为我会添加它。 如果有缺陷请在评论中添加。

如果使用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 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(); } } 

使用Java 7的WatchService ,它是NIO.2的一部分

WatchService API专为需要通知文件更改事件的应用程序而设计。

标准库类不可能实现这一点。 有关详细信息,请参阅此问

为了进行有效的轮询,最好使用随机访问 。 如果你记住文件最后一端的位置并从那里开始阅读,它会有所帮助。

为了扩展Nick Fortescue的最后一个条目,下面是两个可以同时运行的类(例如,在两个不同的shell窗口中),这表明给定的文件可以同时由一个进程写入并由另一个进程读取。

这里,两个进程将执行这些Java类,但我认为编写过程可以来自任何其他应用程序。 (假设它没有对文件进行独占锁定 – 某些操作系统上是否存在此类文件系统锁?)

我已经在Windoze和Linux上成功测试了这两个类。 我非常想知道是否存在一些失败的条件(例如操作系统)。

第1类:

 import java.io.File; import java.io.FileWriter; import java.io.PrintWriter; public class FileAppender { public static void main(String[] args) throws Exception { if ((args != null) && (args.length != 0)) throw new IllegalArgumentException("args is not null and is not empty"); File file = new File("./file.txt"); int numLines = 1000; writeLines(file, numLines); } private static void writeLines(File file, int numLines) throws Exception { PrintWriter pw = null; try { pw = new PrintWriter( new FileWriter(file), true ); for (int i = 0; i < numLines; i++) { System.out.println("writing line number " + i); pw.println("line number " + i); Thread.sleep(100); } } finally { if (pw != null) pw.close(); } } } 

第2课:

 import java.io.BufferedReader; import java.io.File; import java.io.FileReader; public class FileMonitor { public static void main(String[] args) throws Exception { if ((args != null) && (args.length != 0)) throw new IllegalArgumentException("args is not null and is not empty"); File file = new File("./file.txt"); readLines(file); } private static void readLines(File file) throws Exception { BufferedReader br = null; try { br = new BufferedReader( new FileReader(file) ); while (true) { String line = br.readLine(); if (line == null) { // end of file, start polling System.out.println("no file data available; sleeping.."); Thread.sleep(2 * 1000); } else { System.out.println(line); } } } finally { if (br != null) br.close(); } } } 

不幸的是,可用于监视文件末尾的TailInputStream类不是标准Java平台类之一,但Web上的实现很少。 您可以在http://www.greentelligent.com/java/tailinputstream上找到TailInputStream类的实现以及用法示例。

轮询,无论是一致的周期还是随机周期; 200-2000ms应该是一个很好的随机轮询间隔跨度。

检查两件事……

如果你必须注意文件增长,那么检查EOF /字节数,并确保将其与fileAccess或fileWrite时间与lass poll进行比较。 如果(>),则写入文件。

然后,将其与检查独占锁定/读取访问权限相结合。 如果文件可以被读锁定并且它已经增长,那么写入它的任何内容都已完成。

单独检查任一属性并不一定能使您获得保证写入的++状态并且实际上已完成且可供使用。