在查看java中的更改目录时,请避免检测不完整的文件

时间:2011-01-19 23:33:20

标签: java file-io

我正在观看传入文件的目录(使用来自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);
    }
}

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

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

最好的方法是什么?

5 个答案:

答案 0 :(得分:7)

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

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

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

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

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

除非您有一些文件系统限制和保证,否则我认为您无法实现所需。例如,如果您有以下情况怎么办?

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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