web-dev-qa-db-fra.com

fs.watch s'est déclenché deux fois lorsque j'ai modifié le fichier surveillé

 fs.watch( 'example.xml', function ( curr, prev ) {
   // on file change we can read the new xml
   fs.readFile( 'example.xml','utf8', function ( err, data ) {
     if ( err ) throw err;
     console.dir(data);
     console.log('Done');
   });
 });

SORTIE:

  • quelques données
  • Fait X 1
  • quelques données
  • Fait X 2

C'est ma faute d'utilisation ou ..? 

25

L'API fs.watch:

  1. est instable
  2. a connu "comportement" en ce qui concerne les notifications répétées. En particulier, le cas windows résultant de la conception de Windows, dans laquelle une modification de fichier unique peut consister en plusieurs appels à l'API Windows.
33
Pero P.

Je tiens compte de cela en procédant comme suit:

var fsTimeout

fs.watch('file.js', function(e) {

    if (!fsTimeout) {
        console.log('file.js %s event', e)
        fsTimeout = setTimeout(function() { fsTimeout=null }, 5000) // give 5 seconds for multiple events
    }
}
14
jwymanm

Je suggère de travailler avec chokidar ( https://github.com/paulmillr/chokidar ) qui est beaucoup mieux que fs.watch:

Commentant son fichier README.md:

Node.js fs.watch:

  • Ne rapporte pas les noms de fichiers sous OS X.
  • Ne rapporte pas d'événements du tout lors de l'utilisation d'éditeurs tels que Sublime sous OS X.
  • Rapporte souvent des événements deux fois.
  • Emet la plupart des modifications sous la forme rename.
  • A beaucoup d'autres problèmes
  • Ne fournit pas un moyen facile de regarder récursivement les arbres de fichiers.

Node.js fs.watchFile:

  • Presque aussi mauvais à la gestion de l'événement.
  • Aussi ne fournit pas de surveillance récursive.
  • Il en résulte une utilisation élevée du processeur.
12
Artisan72

Si vous devez surveiller votre fichier pour les modifications, vous pouvez consulter ma petite bibliothèque on-file-change . Il vérifie le hachage du fichier sha1 entre les événements change déclenchés.

Explication de la raison pour laquelle nous avons plusieurs événements déclenchés:

Vous remarquerez peut-être que dans certaines situations, un seul événement de création génère plusieurs événements créés gérés par votre composant. Par exemple, si vous utilisez un composant FileSystemWatcher pour surveiller la création de nouveaux fichiers dans un répertoire, puis que vous le testez à l'aide du Bloc-notes pour créer un fichier, deux événements créés peuvent être générés même si un seul fichier a été créé. En effet, le Bloc-notes effectue plusieurs actions sur le système de fichiers pendant le processus d’écriture. Le Bloc-notes écrit sur le disque par lots qui créent le contenu du fichier, puis les attributs de fichier. D'autres applications peuvent fonctionner de la même manière. Étant donné que FileSystemWatcher surveille les activités du système d'exploitation, tous les événements déclenchés par ces applications seront récupérés.

La source

8
Jan Święcki

Je traite de cette question pour la première fois, donc toutes les réponses à ce jour sont probablement meilleures que ma solution. Cependant, aucune d’entre elles ne convenait parfaitement à mon cas. J’ai donc proposé quelque chose de légèrement différent - j’ai utilisé XOR opération permettant d'inverser un entier compris entre 0 et 1, en gardant effectivement une trace et en ignorant chaque deuxième événement du fichier:

var targetFile = "./watchThis.txt"; 
var flippyBit = 0; 

fs.watch(targetFile, {persistent: true}, function(event, filename) {

      if (event == 'change'){
        if (!flippyBit) {
          var data = fs.readFile(targetFile, "utf8", function(error, data) {
            gotUpdate(data);
          })
        } else {
          console.log("Doing nothing thanks to flippybit.");              
        }
        flipBit(); // call flipBit() function
      }
    });

// Whatever we want to do when we see a change
function gotUpdate(data) {
    console.log("Got some fresh data:");
    console.log(data);
    }


// Toggling this gives us the "every second update" functionality

function flipBit() {
    flippyBit = flippyBit ^ 1;
}

Je ne voulais pas utiliser une fonction temporelle (telle que la réponse de jwymanm), car le fichier que je suis en train de regarder pourrait hypothétiquement obtenir des mises à jour légitimes très fréquemment. Et je ne voulais pas utiliser une liste de fichiers surveillés comme le suggère Erik P, car je ne regarde qu'un fichier. La solution de Jan Święcki semblait exagérée, car je travaille sur des fichiers extrêmement courts et simples dans un environnement à faible consommation d'énergie. Enfin, la réponse de Bernado me rendait un peu nerveuse - elle ne pourrait ignorer la deuxième mise à jour que si elle arrivait avant la fin du traitement de la première, et je ne peux pas gérer ce type d'incertitude. Si quelqu'un se retrouvait dans ce scénario très spécifique, l'approche que j'avais utilisée aurait peut-être un sens. Si quelque chose ne va pas, veuillez me le faire savoir/éditer cette réponse, mais jusqu'à présent, elle semble bien fonctionner?

NOTE: Évidemment, ceci fortement suppose que vous obtiendrez exactement 2 événements par changement réel. J'ai soigneusement testé cette hypothèse, évidemment, et appris ses limites. Jusqu'à présent, j'ai confirmé que:

  • Modification d'un fichier dans l'éditeur Atom et enregistrement des déclencheurs 2 mises à jour
  • touch triggers 2 mises à jour
  • La redirection de sortie via > (écrasement du contenu du fichier) déclenche 2 mises à jour
  • Ajout via >>déclenche parfois 1 mise à jour!*

Je peux penser parfaitement aux bonnes raisons pour les différents comportements mais nous n'avons pas besoin de savoir pourquoi il se passe quelque chose pour planifier cela - je voulais juste souligner que vous voudrez vérifier par vous-même. propre environnement et dans le contexte de vos propres cas d'utilisation (duh) et ne pas faire confiance à un imbécile auto-avoué sur Internet. Cela étant dit, avec les précautions prises, je n’ai encore eu aucune bizarrerie.

* Divulgation complète, je ne sais pas vraiment pourquoi cela se produit, mais nous avons déjà un comportement imprévisible avec la fonction watch (), alors qu’est-ce qu’un peu plus d’incertitude? Pour ceux qui suivent chez eux, les ajouts plus rapides à un fichier semblent en empêcher la double mise à jour, mais honnêtement, je ne le sais pas vraiment, et je suis à l'aise avec le comportement de cette solution dans le cas réel. être utilisé, qui est un fichier d’une ligne qui sera mis à jour (le contenu sera remplacé) au moins deux fois par seconde.

1
Toadfish

le premier est le changement et le second est renommé

nous pouvons faire une différence avec la fonction auditeur

function(event, filename) {

}

Le rappel de l'auditeur obtient deux arguments (event, filename). event est 'rename' ou 'change', et filename est le nom du fichier qui a déclenché l'événement.

// rm sourcefile targetfile
fs.watch( sourcefile_dir , function(event, targetfile)){
    console.log( targetfile, 'is', event)
}

comme un fichier source est renommé en fichier cible, il appellera trois événements comme un fait

null is rename // sourcefile not exist again
targetfile is rename
targetfile is change

remarquez que si vous voulez attraper tous ces trois evnet, regardez le répertoire de sourcefile

1
liandong

J'obtiens parfois des enregistrements multiples de l'événement Watch, ce qui provoque le déclenchement de l'événement Watch à plusieurs reprises ... Je l'ai résolu en conservant une liste des fichiers en cours de surveillance et en évitant d'enregistrer l'événement si le fichier était déjà dans la liste:

 var watchfiles = {};

function initwatch(fn, callback) {
    if watchlist[fn] {
        watchlist[fn] = true;
        fs.watch(fn).on('change', callback);
    }
}

......

0
Erik P

La solution la plus simple:

const watch = (path, opt, fn) => {
  var lock = false
  fs.watch(path, opt, function () {
    if (!lock) {
      lock = true
      fn()
      setTimeout(() => lock = false, 1000)
    }
  })
}
watch('/path', { interval: 500 }, function () {
  // ...
})
0
Arthur Araújo

Comme d'autres réponses le dit ... Cela a eu beaucoup de problèmes, mais je peux gérer cela de cette façon:

var folder = "/folder/path/";

var active = true; // flag control

fs.watch(folder, function (event, filename) {
    if(event === 'rename' && active) { //you can remove this "check" event
        active = false;

        // ... its just an example
        for (var i = 0; i < 100; i++) {
            console.log(i);
        }

        // ... other stuffs and delete the file
        if(!active){
            try {
                fs.unlinkSync(folder + filename);
            } catch(err) {
                console.log(err);
            }
            active = true
        }
    }
});

J'espère pouvoir vous aider ...

0
carlitoxenlaweb

Similaire/même problème. Je devais faire des choses avec des images quand elles ont été ajoutées à un répertoire. Voici comment j'ai traité le double tir:

var fs = require('fs');
var working = false;

fs.watch('directory', function (event, filename) {
  if (filename && event == 'change' && active == false) {
    active = true;

    //do stuff to the new file added

    active = false;
});

Il ignorera le deuxième tir jusqu'à ce que termine le nouveau fichier.

0
Bernardo SOUSA

Ma solution personnalisée

Personnellement, j’aime bien utiliser return pour empêcher l’exécution d’un bloc de code lors de la vérification de quelque chose. Voici donc ma méthode:

var watching = false;
fs.watch('./file.txt', () => {
    if(!watching) return;
    watching = true;

    // do something

    // the timeout is to prevent the script to run twice with short functions
    // the delay can be longer to disable the function for a set time
    setTimeout(() => {
        watching = false;
    }, 100);
};

N'hésitez pas à utiliser cet exemple pour simplifier votre code. PAS _ peut être préférable à l'utilisation d'un module fourni par d'autres, mais cela fonctionne plutôt bien!

0
Lasse Brustad