Usando FileSystemWatcher para monitorear un directorio

101

Estoy usando una aplicación de Windows Forms para monitorear un directorio y mover los archivos colocados en él a otro directorio.

Por el momento, copiará el archivo a otro directorio, pero cuando se agregue otro archivo, terminará sin mensaje de error. A veces copia dos archivos antes de terminar en el tercero.

¿Se debe a que estoy usando una aplicación de Windows Form en lugar de una aplicación de consola? ¿Hay alguna manera de evitar que el programa finalice y seguir viendo el directorio?

private void watch()
{
  this.watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                         | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  watcher.Filter = "*.*";
  watcher.Changed += OnChanged;
  watcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
  //Copies file to another directory.
}

public void Dispose()
{
  // avoiding resource leak
  watcher.Changed -= OnChanged;
  this.watcher.Dispose();
}
quesero
fuente

Respuestas:

144

El problema fueron los filtros de notificación. El programa intentaba abrir un archivo que todavía se estaba copiando. Eliminé todos los filtros de notificación excepto LastWrite.

private void watch()
{
  FileSystemWatcher watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastWrite;
  watcher.Filter = "*.*";
  watcher.Changed += new FileSystemEventHandler(OnChanged);
  watcher.EnableRaisingEvents = true;
}
quesero
fuente
6
Hola, estaba usando este enfoque, pero cuando copio un archivo, el evento se genera dos veces: una vez cuando el archivo se crea vacío (comienza la copia) y una vez más cuando finaliza la copia. ¿Cómo evitar este evento duplicado, algún filtro capaz de manejarlo sin un control personalizado de eso?
dhalfageme
@dhalfageme Verifico ambos eventos si aparece algo significativo para mi aplicación en la carpeta.
Eftekhari
30

No proporcionó el código de manejo de archivos, pero supongo que cometió el mismo error que todos cometen al escribir por primera vez tal cosa: el evento filewatcher se generará tan pronto como se cree el archivo. Sin embargo, el archivo tardará algún tiempo en completarse. Tome un tamaño de archivo de 1 GB, por ejemplo. El archivo puede ser creado por otro programa (Explorer.exe copiándolo desde algún lugar) pero tomará unos minutos terminar ese proceso. El evento se genera en el momento de la creación y debe esperar a que el archivo esté listo para copiarse.

Puede esperar a que un archivo esté listo utilizando esta función en un bucle.

nvoigt
fuente
25

La razón puede ser que el observador se declara como variable local para un método y se recolecta como basura cuando finaliza el método. Debería declararlo como miembro de la clase. Intente lo siguiente:

FileSystemWatcher watcher;

private void watch()
{
  watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                         | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  watcher.Filter = "*.*";
  watcher.Changed += new FileSystemEventHandler(OnChanged);
  watcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
  //Copies file to another directory.
}
user1912419
fuente
18
watcherLa variable se mantiene viva (no recolectada basura) porque se suscribió al evento Cambiado.
adospace
1
Creo que en realidad se debe a que EnableRaisingEvents está configurado en true. No creo que el estado de los controladores de eventos de miembros tenga que ver con la recolección de basura. Creo que EnableRaisingEvents tiene, lo que podría llamar favorablemente, un comportamiento especial en este caso.
Matias Grioni