2011-01-19 9 views
7

Estoy viendo un directorio para archivos entrantes (usando FileAlterationObserver desde Apache commons).Evite detectar archivos incompletos al mirar un directorio para ver cambios en Java

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

Los archivos están escritos en el lugar por procesos que no tengo control.

El problema que tengo con ese código es que mi devolución de llamada se desencadena cuando se crea el archivo inicialmente. Necesito que se active cuando se haya cambiado el archivo y se haya completado la escritura en el archivo. (tal vez al detectar cuando el archivo detuvo cambiando)

¿Cuál es la mejor manera de hacerlo?

+0

IOW ¿Desea saber cuándo el proceso de escritura cierra el archivo? – finnw

+0

Creo que básicamente necesito una implementación de [rebote] (http://documentcloud.github.com/underscore/docs/underscore.html#section-52) para Java – levinalex

+0

volver a cerrar el archivo: Sí, si eso funciona que resolvería eso. (¿Hay alguna manera de obtener esa información sin ningún control sobre el escritor?) ¿Podría tratar de obtener un bloqueo exclusivo en el archivo? ¿Eso funciona en todas las plataformas? – levinalex

Respuesta

1

No creo que pueda lograr lo que desea a menos que tenga algunas limitaciones y garantías del sistema de archivos. Por ejemplo, ¿qué pasaría si usted tiene el siguiente escenario:

  • Archivo X creado
  • Un montón de eventos de cambio se desencadenan que se corresponden con la escritura de archivos X
  • Una gran cantidad de tiempo pasa sin cambios a archivo X
  • El archivo X se ha actualizado.

Si el archivo X no puede ser actualizado después de que ha escrito a cabo, puede tener un hilo de ejecución que calcula el tiempo transcurrido desde la última actualización de ahora, y después de un cierto intervalo decide que el archivo de escritura es completa. Pero incluso esto tiene problemas. Si el sistema de archivos está bloqueado y la escritura no se produce durante un tiempo, puede concluir erróneamente que el archivo ha terminado de escribirse.

+1

Tienes razón. Pero, afortunadamente, esto no sucede en mi caso. (Pasos 1 a 3 suceden, Paso 4 no) El archivo está escrito a 'irrazonablemente rápido' – levinalex

+0

En ese caso, puede hacer lo que dije. Todavía hay inconvenientes en este enfoque. Por un lado, necesitarías un hilo por archivo. Otro, es elegir un valor heurístico para el tiempo transcurrido. –

+0

Sí. Hice una pregunta de seguimiento [Debounce en Java] (http://stackoverflow.com/questions/4742210/implementing-debounce-in-java) para la solución de subprocesos. ("irrazonablemente rápido" es de 100ms en mi caso). Estoy realmente interesado si el enfoque de bloqueo funcionaría porque sería mucho más elegante. – levinalex

1

Puede verificar el tamaño del archivo 2 o más veces en un par de segundos y si el tamaño no cambia, puede decidir que el cambio de archivo se ha completado y proceder con su propia ejecución.

7

Tuve un problema similar. Al principio pensé que podría usar el servicio FileWatcher, pero no funciona en volúmenes remotos, y tuve que controlar los archivos entrantes a través de una unidad montada en la red.

Luego pensé que podría simplemente controlar el cambio en el tamaño del archivo durante un período de tiempo y considerar el archivo hecho una vez que el tamaño del archivo se haya estabilizado (como sugirió fmucar). Pero descubrí que, en algunos casos, en archivos grandes, el sistema de host informaría el tamaño completo del archivo que estaba copiando, en lugar de la cantidad de bytes que había escrito en el disco. Esto, por supuesto, hizo que el archivo pareciera estable, y mi detector captaría el archivo mientras todavía estaba en proceso de escritura.

Eventualmente logré que el monitor funcionara, mediante el uso de una excepción FileInputStream, que funcionó maravillosamente al detectar si se estaba escribiendo un archivo, incluso cuando el archivo estaba en una unidad montada en la red.

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

¿Por qué aún comprobar el tamaño? ¿Por qué no solo verifica si está abierto? – Steve

+0

Creo que sería mejor no usar un FileInputStream anónimo, por lo que puede cerrarlo después del ciclo. De lo contrario, ¡esto me funcionó muy bien! – Steve

0

Si utiliza FileAlterationListener y añadir un FileAlterationListenerAdaptor se puede poner en práctica los métodos que necesita y supervisar los archivos con un 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(); 
} 
+0

hola, esta es la manera de implementar cambios de archivos, pero no maneja el caso cuando el intervalo de sondeo es menor que el tiempo necesario para copiar todo el archivo de una ubicación de red a otra. Ya veo este problema en nuestro código, por lo que es necesario hacer algunas comprobaciones adicionales o esperar el ciclo para que el sistema de archivos desencadene el archivo y luego vaya al adaptador, el adaptador debe esperar hasta que se complete el archivo. –

2

Una solución genérica a este problema parece imposible desde el " consumidor "fin. El "productor" puede cerrar temporalmente el archivo y luego volver a agregarlo. O el "productor" puede bloquearse, dejando un archivo incompleto en el sistema de archivos.

Un patrón razonable es hacer que el "productor" escriba en un archivo temporal que no está supervisado por el "consumidor". Cuando termine de escribir, cambie el nombre del archivo a algo que esté realmente monitoreado por el "consumidor", momento en el cual el "consumidor" recogerá el archivo completo.

Cuestiones relacionadas