32 votes

Java : Surveiller un répertoire pour déplacer des fichiers volumineux

J'ai écrit un programme qui surveille un répertoire et lorsque des fichiers y sont créés, il en change le nom et les déplace dans un nouveau répertoire. Dans ma première implémentation, j'ai utilisé l'API Watch Service de Java, ce qui a bien fonctionné lorsque je testais des fichiers de 1 kb. Le problème qui s'est présenté est qu'en réalité, les fichiers créés sont de 50 à 300 Mo. Dans ce cas, l'API de surveillance trouvait immédiatement le fichier mais ne pouvait pas le déplacer car il était toujours en cours d'écriture. J'ai essayé de placer l'observateur dans une boucle (qui générait des exceptions jusqu'à ce que le fichier puisse être déplacé) mais cela semblait plutôt inefficace.

Comme cela n'a pas fonctionné, j'ai essayé d'utiliser un timer qui vérifie le dossier toutes les 10s et déplace les fichiers quand il le peut. C'est la méthode que j'ai fini par adopter.

Question : Existe-t-il un moyen de signaler la fin de l'écriture d'un fichier sans vérifier les exceptions ou comparer continuellement la taille ? J'aime l'idée d'utiliser l'API Watcher une seule fois pour chaque fichier au lieu de vérifier continuellement avec un timer (et de rencontrer des exceptions).

Toutes les réponses sont très appréciées !

nt

3voto

Bien qu'il ne soit pas possible d'être notifié par l'API du service de surveillance lorsque la copie de l'OS est terminée, toutes les options semblent être des "solutions de contournement" (y compris celle-ci !).

Comme indiqué ci-dessus,

1) Le déplacement ou la copie n'est pas une option sous UNIX ;

2) File.canWrite renvoie toujours true si vous avez la permission d'écrire, même si le fichier est toujours en cours de copie ;

3) Attendre jusqu'à ce qu'un délai d'attente ou qu'un nouvel événement se produise serait une option, mais que se passe-t-il si le système est surchargé mais que la copie n'est pas terminée ? si le délai d'attente est une grande valeur, le programme attendra très longtemps.

4) L'écriture d'un autre fichier pour "signaler" que la copie est terminée n'est pas une option si vous ne faites que consommer le fichier, sans le créer.

Une alternative est d'utiliser le code ci-dessous :

boolean locked = true;

while (locked) {
    RandomAccessFile raf = null;
    try {
            raf = new RandomAccessFile(file, "r"); // it will throw FileNotFoundException. It's not needed to use 'rw' because if the file is delete while copying, 'w' option will create an empty file.
            raf.seek(file.length()); // just to make sure everything was copied, goes to the last byte
            locked = false;
        } catch (IOException e) {
            locked = file.exists();
            if (locked) {
                System.out.println("File locked: '" + file.getAbsolutePath() + "'");
                Thread.sleep(1000); // waits some time
            } else { 
                System.out.println("File was deleted while copying: '" + file.getAbsolutePath() + "'");
            }
    } finally {
            if (raf!=null) {
                raf.close();    
            }
        }
}

0voto

Stefan Points 11

Il s'agit d'une très intéressant Il s'agit en effet d'un cas d'utilisation courant : attendre la création d'un nouveau fichier, puis réagir au fichier d'une manière ou d'une autre. La condition de course ici est intéressante, car la condition de haut niveau est certainement d'obtenir un événement et ensuite d'obtenir (au moins) un verrou de lecture sur le fichier. Avec des fichiers volumineux ou simplement beaucoup de créations de fichiers, cela pourrait nécessiter tout un pool de threads de travail qui essaient périodiquement d'obtenir des verrous sur les fichiers nouvellement créés et, lorsqu'ils réussissent, effectuent réellement le travail. Mais comme NT s'en rend compte, je suis sûr qu'il faudrait faire cela avec précaution pour le rendre évolutif, car il s'agit finalement d'une approche d'interrogation, et l'évolutivité et l'interrogation ne sont pas deux mots qui vont bien ensemble.

0voto

Ramcis Points 41

J'ai dû faire face à une situation similaire lorsque j'ai implémenté un observateur de système de fichiers pour transférer des fichiers téléchargés. La solution que j'ai mise en place pour résoudre ce problème consiste en ce qui suit :

1- Tout d'abord, maintenez une carte des fichiers non traités (Tant que le fichier est toujours copié, le système de fichiers génère des Modify_Event, vous pouvez donc les ignorer si le drapeau est faux).

2- Dans votre fileProcessor, vous prenez un fichier dans la liste et vérifiez s'il est verrouillé par le système de fichiers, si oui, vous obtiendrez une exception, il suffit d'attraper cette exception et de mettre votre thread en état d'attente (c'est à dire 10 secondes) puis de réessayer jusqu'à ce que le verrou soit libéré. Après avoir traité le fichier, vous pouvez soit changer l'indicateur en vrai, soit le supprimer de la carte.

Cette solution ne sera pas efficace si de nombreuses versions d'un même fichier sont transférées pendant le temps d'attente.

Santé, Ramzi

0voto

Eric B. Points 3460

Selon l'urgence avec laquelle vous devez déplacer le fichier une fois qu'il a fini d'être écrit, vous pouvez également vérifier la stabilité de l'horodatage de la dernière modification et ne déplacer le fichier que lorsqu'il est mis au repos. La durée de stabilité peut dépendre de l'implémentation, mais je suppose qu'un fichier dont l'horodatage de dernière modification n'a pas changé pendant 15 secondes devrait être suffisamment stable pour être déplacé.

0voto

Pawan Kumar Points 492

Pour les gros fichiers sous Linux, les fichiers sont copiés avec une extension de .filepart. Vous devez juste vérifier l'extension en utilisant l'api commons et enregistrer l'événement ENTRY_CREATE. J'ai testé cela avec mes fichiers .csv (1GB) et cela a fonctionné.

public void run()
{
    try
    {
        WatchKey key = myWatcher.take();
        while (key != null)
        {
            for (WatchEvent event : key.pollEvents())
            {
                if (FilenameUtils.isExtension(event.context().toString(), "filepart"))
                {
                    System.out.println("Inside the PartFile " + event.context().toString());
                } else
                {
                    System.out.println("Full file Copied " + event.context().toString());
                    //Do what ever you want to do with this files.
                }
            }
            key.reset();
            key = myWatcher.take();
        }
    } catch (InterruptedException e)
    {
        e.printStackTrace();
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X