2010-11-25 24 views
5

Estoy desarrollando una aplicación .NET, donde estoy usando la clase FileSystemWatcher y adjunto su evento Creado en una carpeta. Tengo que hacer algo sobre este evento (es decir, copiar el archivo a otra ubicación). Cuando coloco un tamaño grande en la carpeta del reloj adjunto, el evento se levantó inmediatamente, incluso el proceso de copia de archivo aún no se completó. No quiero verificar esto por el método file.open.C# FileSystemWatcher, Cómo saber cómo se copió el archivo por completo en la carpeta del reloj

¿Hay alguna manera de recibir notificaciones de que mi proceso de copia de archivos en la carpeta del reloj se ha completado y luego mi evento se dispara?

+0

Tuve el mismo problema, y ​​además FileSystemWatcher no parece funcionar correctamente en Windows 7, a veces no obtengo el evento fireing –

Respuesta

1

Puede escuchar el evento modificado e iniciar un temporizador. Si el evento modificado vuelve a subir, reinicie el temporizador. Cuando el temporizador ha alcanzado un cierto valor sin que se haya producido el evento de modificación, puede intentar realizar la copia.

0

intenta establecer filtros

myWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite; 
+0

Esto no funcionará. El evento se generará varias veces (una vez por cada acceso de escritura mientras se copia el archivo), y no tiene forma de saber si esa operación de escritura fue la última que completó la copia. – gstercken

+0

Compruebo si el archivo tiene acceso de escritura en el evento, y decido si la copia está terminada o no –

2

que he tenido el mismo problema y lo resolvió de esta manera:

  1. Establecer FileSystemWatcher para notificar cuando los archivos se crean y cuando se modifican .
  2. Cuando llega una notificación:

    a. Si no hay un temporizador configurado para este nombre de archivo (ver a continuación), configure un temporizador para que expire en un intervalo adecuado (generalmente uso 1 segundo).

    b. Si hay un temporizador configurado para este nombre de archivo, cancele el temporizador y configure uno nuevo para que expire en el mismo intervalo.

Cuando expira un temporizador, ustedes saben que el archivo asociado se ha creado o modificado y ha sido tocado por el intervalo de tiempo. Esto significa que es probable que la copia/modificación esté lista y ahora puede procesarla.

+0

¿podría compartirme su código de temporizador – user446526

+0

¿puede publicar algo aquí? gracias – Poorna

+0

@Puma: No es posible para este pedazo de código, lo siento. – Jon

3

De hecho, es un fastidio que FileSystemWatcher (y el subyacente API ReadDirectoryChangesW) proporcionan ninguna manera para ser notificado cuando un nuevo archivo ha sido totalmente creado.

La forma mejor y más segura en todo esto que me he encontrado hasta el momento (y que no se basa en los temporizadores) dice así:

Al recibir el evento creado, iniciar un hilo que, en una loop, comprueba si el archivo aún está bloqueado (utilizando un intervalo de reintento apropiado y un recuento de reintento máximo). La única manera de comprobar si un archivo está bloqueado es intentando abrirlo con acceso exclusivo: si tiene éxito (sin lanzar una IOException), entonces el archivo está listo para copiarse, y su hilo puede generar un evento apropiado (por ejemplo, FileCopyCompleted).

1

Me suscribo al Changed - y Renamed -evento e intento cambiar el nombre del archivo en cada Changed -event capturando las IOExcepciones. Si el cambio de nombre tiene éxito, la copia ha finalizado y el evento Rename -event se activa solo una vez.

1

Tres problemas con FileSystemWatcher, la primera es que se puede enviar eventos de la creación duplicados para que compruebe de que con algo como:

this.watcher.Created += (s, e) => 
{ 
    if (!this.seen.ContainsKey(e.FullPath) 
     || (DateTime.Now - this.seen[e.FullPath]) > this.seenInterval) 
    { 
     this.seen[e.FullPath] = DateTime.Now; 
     ThreadPool.QueueUserWorkItem(
      this.WaitForCreatingProcessToCloseFileThenDoStuff, e.FullPath); 
    } 
}; 

donde this.seen es una Dictionary<string, DateTime> y this.seenInterval es una TimeSpan.

A continuación, tiene que esperar a que el creador del archivo termine de escribirlo (el problema planteado en la pregunta). Y, tercero, debe tener cuidado porque a veces el evento de creación de archivos se lanza antes de que se pueda abrir el archivo sin darle un FileNotFoundException, pero también se puede quitar antes de que pueda obtenerlo, lo que también da un FileNotFoundException.

private void WaitForCreatingProcessToCloseFileThenDoStuff(object threadContext) 
{ 
    // Make sure the just-found file is done being 
    // written by repeatedly attempting to open it 
    // for exclusive access. 
    var path = (string)threadContext; 
    DateTime started = DateTime.Now; 
    DateTime lastLengthChange = DateTime.Now; 
    long lastLength = 0; 
    var noGrowthLimit = new TimeSpan(0, 5, 0); 
    var notFoundLimit = new TimeSpan(0, 0, 1); 

    for (int tries = 0;; ++tries) 
    { 
     try 
     { 
      using (var fileStream = new FileStream(
       path, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) 
      { 
       // Do Stuff 
      } 

      break; 
     } 
     catch (FileNotFoundException) 
     { 
      // Sometimes the file appears before it is there. 
      if (DateTime.Now - started > notFoundLimit) 
      { 
       // Should be there by now 
       break; 
      } 
     } 
     catch (IOException ex) 
     { 
      // mask in severity, customer, and code 
      var hr = (int)(ex.HResult & 0xA000FFFF); 
      if (hr != 0x80000020 && hr != 0x80000021) 
      { 
       // not a share violation or a lock violation 
       throw; 
      } 
     } 

     try 
     { 
      var fi = new FileInfo(path); 
      if (fi.Length > lastLength) 
      { 
       lastLength = fi.Length; 
       lastLengthChange = DateTime.Now; 
      } 
     } 
     catch (Exception ex) 
     { 
     } 

     // still locked 
     if (DateTime.Now - lastLengthChange > noGrowthLimit) 
     { 
      // 5 minutes, still locked, no growth. 
      break; 
     } 

     Thread.Sleep(111); 
    } 

Puede, por supuesto, establecer sus propios tiempos de espera. Este código deja suficiente tiempo para un bloqueo de 5 minutos. El código real también tendría una bandera para salir del hilo si así lo solicita.

0

Esta respuesta es un poco tarde, pero si fuera posible obtendría el proceso de origen para copiar un pequeño archivo de marcador después del archivo grande o archivos y usar FileWatcher en eso.

Cuestiones relacionadas