2012-04-05 25 views
22

Digamos que tiene un proceso externo para escribir archivos en algún directorio, y tiene un proceso separado tratando periódicamente de leer archivos de este directorio. El problema para evitar es leer un archivo que el otro proceso está actualmente escribiendo, por lo que estaría incompleto. Actualmente, el proceso que se lee utiliza una verificación mínima del temporizador de edad del archivo, por lo que ignora todos los archivos a menos que su última fecha de modificación tenga más de XX segundos de antigüedad.Cómo comprobar si un archivo está "completo" (escrito por completo) con Java

Me pregunto si hay una manera más clara de resolver este problema. Si el tipo de archivo es desconocido (podría tratarse de varios formatos diferentes), ¿hay alguna forma confiable de verificar el encabezado del archivo para la cantidad de bytes que deberían estar en el archivo, frente al número de bytes actualmente en el archivo para confirmar que coinciden?

¡Gracias por cualquier idea o pensamiento!

+1

¿Tiene alguna control sobre el proceso de escritura de archivos en el directorio que eres ¿acecho? –

+0

Además de renombrar el archivo cuando haya terminado, el enfoque que tomo es para que sea correcto leer el archivo tal como se está escribiendo (piense 'tail' en Unix) –

Respuesta

9

Puede utilizar un archivo de marcador externo. El proceso de escritura podría crear un archivo XYZ.lock antes de que comience a crear el archivo XYZ, y eliminar XYZ.lock después de que se complete XYZ. El lector entonces sabría fácilmente que puede considerar que un archivo está completo solo si el archivo .lock correspondiente no está presente.

+0

Hola Michal, ¿cómo podemos verificar que el" archivo está bloqueado " "a través del programa. –

+0

Aquí, no hay bloqueos adicionales en el archivo; el hecho de que exista o no un archivo es lo que constituye el bloqueo. –

+1

¿Qué sucede si no tiene control sobre el proceso de escritura? – Matthieu

2

Incluso el número de bytes es igual, el contenido del archivo puede ser diferente.

Creo que debe coincidir con el byte de byte viejo y nuevo del archivo.

1

2 opciones que parece resolver este problema:

  1. la mejor opción-escritor proceso de notificar proceso de lectura de alguna manera que la escritura se terminó.
  2. escribe el archivo en {id} .tmp, que al finalizar, renómbralo a {id} .java y el proceso de lectura solo se ejecutará en los archivos * .java. El cambio de nombre requiere mucho menos tiempo y la probabilidad de que estos 2 procesos trabajen juntos disminuye.
1

En primer lugar, está Why doesn't OS X lock files like windows does when copying to a Samba share?, pero eso es una variación de lo que ya está haciendo.

En cuanto a la lectura de archivos arbitrarios y la búsqueda de tamaños, algunos archivos tienen esa información, otros no, pero incluso aquellos que no tienen una forma común de representarla. Necesitará información específica de cada formato y los gestionará de forma independiente.

Si tiene que actuar en el archivo "instantáneamente", su proceso de escritura debería enviar algún tipo de notificación. De lo contrario, está bastante atrapado en el sondeo de los archivos, y leer el directorio es bastante barato en términos de E/S en comparación con la lectura de bloques aleatorios de archivos aleatorios.

8

La forma en que he hecho esto en el pasado es que el proceso de escribir el archivo escribe en un archivo "temporal" y luego mueve el archivo a la ubicación de lectura cuando termina de escribir el archivo.

De modo que el proceso de escritura escribiría en info.txt.tmp. Cuando termina, cambia el nombre del archivo a info.txt. El proceso de lectura solo tuvo que verificar la existencia de info.txt - y sabe que si existe, se ha escrito por completo.

Como alternativa podría tener el proceso de escritura escribir información.txt a un directorio diferente, y luego muévalo al directorio de lectura si no le gusta usar extensiones de archivos extrañas.

2

Una solución sencilla que he utilizado en el pasado para este escenario con Windows es utilizar boolean File.renameTo(File) y tratar de mover el archivo original a una carpeta de ensayo por separado:

Si es successfalse, entonces el potentiallyIncompleteFile todavía se está escribiendo en.

2

No tuve la opción de usar marcadores temporales, etc. ya que los clientes cargan los archivos a través del par de llaves SFTP. pueden ser muy grandes en tamaño.

Es bastante raro pero comparo el tamaño del archivo antes y después de dormir unos segundos.

Su obviamente no es ideal para bloquear el hilo, pero en nuestro caso no es más que se ejecuta como un sistema de procesos de fondo así que parece funcionar bien

private boolean isCompletelyWritten(File file) throws InterruptedException{ 
    Long fileSizeBefore = file.length(); 
    Thread.sleep(3000); 
    Long fileSizeAfter = file.length(); 

    System.out.println("comparing file size " + fileSizeBefore + " with " + fileSizeAfter); 

    if (fileSizeBefore.equals(fileSizeAfter)) { 
     return true; 
    } 
    return false; 
} 

Nota: como se menciona más adelante que esto podría no funcionar en las ventanas. Esto fue utilizado en un entorno Linux.

+0

El único punto de error sería un bloqueo de red – Skynet

+0

Este código fallará ya que los metadatos de tamaño de archivo se escriben como el primer paso en Windows. Por lo tanto, siempre file.length() es el mismo – debugger89

0

Esto es posible haciendo usando la biblioteca maven Apache Commons IO del método FileUtils.copyFile(). Si intenta copiar el archivo y obtener IOException, significa que el archivo no está completamente guardado.

Ejemplo:

public static void copyAndDeleteFile(File file, String destinationFile) { 

    try { 
     FileUtils.copyFile(file, new File(fileDirectory)); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     copyAndDeleteFile(file, fileDirectory, delayThreadPeriod); 
    } 

o periódicamente comprobar con cierto tamaño retraso de la carpeta que contiene este archivo:

FileUtils.sizeOfDirectory(folder); 
+0

. Es interesante cómo Commons IO puede rastrear esto. Entonces esto probablemente responda la pregunta original sin una copia complicada antes. – Thomas

Cuestiones relacionadas