在观察java中的更改目录时,避免检测不完整的文件

我正在查看传入文件的目录(使用来自apache commons的FileAlterationObserver )。

class Example implements FileAlterationListener { public void prepare() { File directory = new File("/tmp/incoming"); FileAlterationObserver observer = new FileAlterationObserver(directory); observer.addListener(this); FileAlterationMonitor monitor = new FileAlterationMonitor(10); monitor.addObserver(observer); monitor.start(); // ... } public void handleFile(File f) { // FIXME: this should be called when the writes that // created the file have completed, not before } public void onFileCreate(File f) { handleFile(f); } public void onFileChange(File f) { handleFile(f); } } 

文件由我无法控制的进程写入。

我对该代码的问题是在最初创建文件时触发了我的回调。 我需要它在文件被更改并且文件写入完成时触发。 (可能通过检测文件何时停止更改)

最好的方法是什么?

我有类似的问题。 起初我以为我可以使用FileWatcher服务,但它不能在远程卷上运行,我必须通过网络安装的驱动器监控传入的文件。

然后我想我可以简单地监视一段时间内文件大小的变化,并在文件大小稳定后考虑文件完成(如fmucar建议的那样)。 但我发现在大型文件的某些情况下,托管系统会报告它正在复制的文件的完整大小,而不是它写入磁盘的字节数。 这当然使文件显得稳定,我的探测器会在文件处于写入过程中时捕获该文件。

我最终能够通过使用FileInputStreamexception使监视器工作,该exception在检测文件是否被写入时非常有效,即使文件位于网络安装的驱动器上也是如此。

  long oldSize = 0L; long newSize = 1L; boolean fileIsOpen = true; while((newSize > oldSize) || fileIsOpen){ oldSize = this.thread_currentFile.length(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } newSize = this.thread_currentFile.length(); try{ new FileInputStream(this.thread_currentFile); fileIsOpen = false; }catch(Exception e){} } System.out.println("New file: " + this.thread_currentFile.toString()); 

从“消费者”端看来,这个问题的通用解决方案似乎是不可能的。 “生产者”可以暂时关闭该文件,然后继续追加该文件。 或者“生产者”可能崩溃,在文件系统中留下不完整的文件。

一个合理的模式是让“生产者”写入一个不受“消费者”监控的临时文件。 完成写入后,将文件重命名为“消费者”实际监控的内容,此时“消费者”将获取完整的文件。

除非你有一些文件系统限制和保证,否则我认为你不能达到你想要的效果。 例如,如果您有以下情况,该怎么办:

  • 文件X已创建
  • 触发一堆与写出文件X相对应的更改事件
  • 很多时间都没有更新到文件X.
  • 文件X已更新。

如果文件X在写出后无法更新,则可以有一个执行线程来计算从上次更新到现在的经过时间,并在一段时间后确定文件写入完成。 但即便如此也存在问题。 如果文件系统挂起,并且写入没有发生一段时间,您可能会错误地断定该文件已完成写出。

您可以在几秒钟内检查文件大小2次或更多次,如果大小没有变化,则可以决定文件更改是否已完成并继续执行。

如果您使用FileAlterationListener并添加FileAlterationListenerAdaptor,您可以实现所需的方法并使用FileAlterationMonitor监视文件…

 public static void main( String[] args ) throws Exception { FileAlterationObserver fao = new FileAlterationObserver( dir ); final long interval = 500; FileAlterationMonitor monitor = new FileAlterationMonitor( interval ); FileAlterationListener listener = new FileAlterationListenerAdaptor() { @Override public void onFileCreate( File file ) { try { System.out.println( "File created: " + file.getCanonicalPath() ); } catch( IOException e ) { e.printStackTrace( System.err ); } } @Override public void onFileDelete( File file ) { try { System.out.println( "File removed: " + file.getCanonicalPath() ); } catch( IOException e ) { e.printStackTrace( System.err ); } } @Override public void onFileChange( File file ) { try { System.out.println( file.getName() + " changed: "); } catch( Exception e ) { e.printStackTrace(); } } }; // Add listeners... fao.addListener( listener ); monitor.addObserver( fao ); monitor.start(); }