2012-09-04 15 views
9

Cuando se está copiando un archivo en la carpeta del observador de archivos, ¿cómo puedo identificar si el archivo está completamente copiado y listo para usar? Porque recibo múltiples eventos durante la copia de archivo. (El archivo se copia a través de otro programa usando File.Copy.)FileSystemWatcher: ¿está listo para usar el archivo?

Respuesta

2

Tuve este problema al escribir un archivo. Recibí eventos antes de que el archivo estuviera completamente escrito y cerrado.

La solución es utilizar un nombre de archivo temporal y renombrar el archivo una vez finalizado. Luego, observe el evento de cambio de nombre del archivo en lugar de la creación del archivo o el evento de cambio.

+1

Esto no funciona si alguien más te empuja el archivo (como sobre FTP o algo así) - tienes que saber que el archivo está listo para usar antes de que puedas cambiarle el nombre y decir que está listo para usar :) – Tacroy

1

Nota: este problema no se puede resolver en caso genérico. Sin conocimiento previo sobre el uso de archivos, no se puede saber si otros programas terminaron de funcionar con el archivo.

En su caso particular, debería ser capaz de averiguar en qué operaciones consiste File.Copy.

El archivo de destino más probable está bloqueado durante toda la operación. En este caso, debería poder simplemente intentar abrir el archivo y manejar la excepción de "violación de modo de compartir".

También puede esperar un tiempo ... - opción muy poco confiable, pero si conoce el rango de tamaño de los archivos, es posible que pueda tener un retraso razonable para que Copie termine.

También puede "inventar" algún tipo de sistema de transacción, es decir, crear otro archivo como "nombre_archivo_destino.COPYLOCK" que programa crearía antes de copiar el "nombre_archivo_destino" y eliminarlo posteriormente.

9

Cuando me encontré con este problema, la mejor solución que se me ocurrió fue tratar continuamente de obtener un bloqueo exclusivo en el archivo; mientras se está escribiendo el archivo, el intento de bloqueo fallará, esencialmente el método en this responder. Una vez que el archivo no se está escribiendo, el bloqueo tendrá éxito.

Desafortunadamente, la única forma de hacerlo es envolviendo/probando la apertura del archivo, lo que me avergüenza: tener que usar try/catch siempre es doloroso. Sin embargo, parece que no hay forma de evitarlo, así que es lo que terminé usando.

Modificación del código en esa respuesta hace el truco, así que terminamos usando algo como esto:

private void WaitForFile(FileInfo file) 
{ 
    FileStream stream = null; 
    bool FileReady = false; 
    while(!FileReady) 
    { 
     try 
     { 
      using(stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)) 
      { 
       FileReady = true; 
      } 
     } 
     catch (IOException) 
     { 
      //File isn't ready yet, so we need to keep on waiting until it is. 
     } 
     //We'll want to wait a bit between polls, if the file isn't ready. 
     if(!FileReady) Thread.Sleep(1000); 
    } 
} 
+3

Es posible que desee agregar un conteo y dejar de intentar x intentos fallidos. – McGarnagle

1

Aquí es un método que va a volver a intentar el acceso a archivos hasta un número X de veces, con un Sleep entre intentos. Si nunca se pone el acceso, la aplicación se mueve en:

private static bool GetIdleFile(string path) 
{ 
    var fileIdle = false; 
    const int MaximumAttemptsAllowed = 30; 
    var attemptsMade = 0; 

    while (!fileIdle && attemptsMade <= MaximumAttemptsAllowed) 
    { 
     try 
     { 
      using (File.Open(path, FileMode.Open, FileAccess.ReadWrite)) 
      { 
       fileIdle = true; 
      } 
     } 
     catch 
     { 
      attemptsMade++; 
      Thread.Sleep(100); 
     } 
    } 

    return fileIdle; 
} 

Puede ser utilizado como esto:

private void WatcherOnCreated(object sender, FileSystemEventArgs e) 
{ 
    if (GetIdleFile(e.FullPath)) 
    { 
     // Do something like... 
     foreach (var line in File.ReadAllLines(e.FullPath)) 
     { 
      // Do more... 
     } 
    } 
} 
0
private Stream ReadWhenAvailable(FileInfo finfo, TimeSpan? ts = null) => Task.Run(() => 
    { 
     ts = ts == null ? new TimeSpan(long.MaxValue) : ts; 
     var start = DateTime.Now; 
     while (DateTime.Now - start < ts) 
     { 
      Thread.Sleep(200); 
      try 
      { 
       return new FileStream(finfo.FullName, FileMode.Open); 
      } 
      catch { } 
     } 
     return null; 
    }) 
    .Result; 

... por supuesto, puede modificar los aspectos de este para adaptarse a su necesariamente.

Cuestiones relacionadas