Comment tester si un fichier est «complet» (complètement écrit) avec Java

Supposons que vous ayez un processus externe écrivant des fichiers dans un répertoire et que vous ayez eu un processus distinct essayant périodiquement de lire les fichiers de ce répertoire. Le problème à éviter est de lire un fichier que l’autre processus est actuellement en train d’écrire, de sorte qu’il serait incomplet. Actuellement, le processus qui lit utilise un minuteur de vérification de l’âge des fichiers minimum. Par conséquent, il ignore tous les fichiers, sauf si leur date de dernière modification est supérieure à XX secondes.

Je me demande s’il existe un moyen plus propre de résoudre ce problème. Si le type de fichier est inconnu (il peut exister un certain nombre de formats différents), existe-t-il un moyen fiable de vérifier le nombre d’octets devant figurer dans l’en-tête du fichier, par rapport au nombre d’octets actuellement présents dans le fichier pour confirmer leur correspondance?

Merci pour vos pensées ou vos idées!

Vous pouvez utiliser un fichier de marqueur externe. Le processus d’écriture pourrait créer un fichier XYZ.lock avant qu’il ne commence à créer le fichier XYZ et supprimer XYZ.lock après avoir terminé XYZ. Le lecteur saurait alors facilement qu’il ne peut considérer un fichier comme complet que si le fichier .lock correspondant n’est pas présent.

Par le passé, le processus d’écriture du fichier est écrit dans un fichier “temp”, puis déplacé le fichier vers l’emplacement de lecture une fois le fichier écrit.

Ainsi, le processus d’écriture écrirait dans info.txt.tmp . Une fois terminé, il renomme le fichier en info.txt . Le processus de lecture devait alors simplement vérifier l’existence de info.txt – et il sait que s’il existe, il a été entièrement écrit.

Alternativement, vous pourriez avoir le processus d’écriture écrire info.txt dans un répertoire différent, puis le déplacer dans le répertoire de lecture si vous n’aimez pas utiliser des extensions de fichiers étranges.

Je n’avais pas la possibilité d’utiliser des marqueurs temporaires, etc., car les fichiers sont téléchargés par les clients via SFTP. ils peuvent être de très grande taille.

C’est assez pirate mais je compare la taille du fichier avant et après avoir dormi quelques secondes.

Ce n’est évidemment pas idéal pour verrouiller le thread, mais dans notre cas, il s’agit simplement d’un processus en arrière-plan qui semble fonctionner correctement.

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

Remarque: comme mentionné ci-dessous, cela pourrait ne pas fonctionner sous Windows. Cela a été utilisé dans un environnement Linux.

Une solution simple que j’ai utilisée par le passé pour ce scénario avec Windows consiste à utiliser le boolean File.renameTo(File) et à tenter de déplacer le fichier d’origine dans un dossier boolean File.renameTo(File) séparé:

 boolean success = potentiallyIncompleteFile.renameTo(stagingAreaFile); 

Si success est false , le potentiallyIncompleteFile est toujours en cours d’écriture.

Même le nombre d’octets est égal, le contenu du fichier peut être différent.

Donc, je pense que vous devez faire correspondre l’ancien et le nouveau fichier octet par octet.

2 options qui semblent résoudre ce problème:

  1. Le meilleur processus d’écriture d’option informe en quelque sorte le processus de lecture que l’écriture est terminée.
  2. écrivez le fichier dans {id} .tmp, puis, une fois terminé, renommez-le en {id} .java et le processus de lecture ne s’exécutera que sur des fichiers * .java. renommer en prenant beaucoup moins de temps et la chance de ce processus 2 travaillent ensemble diminuer.

Tout d’abord, pourquoi les fichiers de locking OS X tels que Windows ne copient-ils pas sur un partage Samba? mais c’est une variation de ce que vous faites déjà.

En ce qui concerne la lecture de fichiers arbitraires et la recherche de tailles, certains fichiers ont cette information, d’autres pas, mais même ceux qui ne le font pas n’ont aucun moyen de le représenter. Vous auriez besoin d’informations spécifiques à chaque format et les gérer chacun indépendamment.

Si vous devez absolument agir sur le fichier “instantanément”, votre processus d’écriture devra alors envoyer une sorte de notification. Sinon, vous êtes plutôt coincé dans le sondage des fichiers, et la lecture du répertoire est relativement peu coûteuse en termes d’E / S par rapport à la lecture de blocs aléatoires à partir de fichiers aléatoires.

Cela est possible en utilisant la méthode FileUtils.copyFile () de la bibliothèque maven Apache Commons IO . Si vous essayez de copier un fichier et obtenez IOException, cela signifie que le fichier n’est pas complètement enregistré.

Exemple:

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

Ou vérifiez périodiquement avec une certaine taille de délai du dossier contenant ce fichier:

 FileUtils.sizeOfDirectory(folder);