2012-09-04 15 views
9

Quando un file viene copiato nella cartella di controllo del file, come posso identificare se il file è stato completamente copiato e pronto per l'uso? Perché sto ricevendo più eventi durante la copia del file. (Il file viene copiato tramite un altro programma utilizzando File.Copy.)FileSystemWatcher - File pronto per l'uso

risposta

2

Ho riscontrato questo problema durante la scrittura di un file. Ho avuto eventi prima che il file fosse completamente scritto e chiuso.

La soluzione è utilizzare un nome di file temporaneo e rinominare il file una volta terminato. Quindi guarda il file rinominare l'evento anziché la creazione del file o cambiare l'evento.

+1

Questo non funziona se qualcun altro sta spingendo il file (come su FTP o qualcosa del genere) - devi sapere che il file è pronto per l'uso prima di poterlo rinominare e dire che è pronto per l'uso :) – Tacroy

1

Nota: questo problema non è risolvibile nel caso generico. Senza una conoscenza preliminare sull'uso dei file, non è possibile sapere se l'esecuzione di altri programmi con il file è terminata.

Nel tuo caso particolare dovresti essere in grado di capire in che cosa consiste File.Copy.

Il file di destinazione più probabile è bloccato durante l'intera operazione. In questo caso dovresti essere in grado di provare semplicemente ad aprire il file e gestire l'eccezione "violazione della modalità di condivisione".

È anche possibile attendere per un po 'di tempo ... - Opzione molto inaffidabile, ma se si conosce l'intervallo di dimensioni dei file si può essere in grado di avere un ritardo ragionevole per consentire a Copia di finire.

È anche possibile "inventare" una sorta di sistema di transazione, ovvero creare un altro file come "destination_file_name.COPYLOCK", il programma che copia il file creato prima di copiare "destination_file_name" ed eliminare in seguito.

9

Quando mi sono imbattuto in questo problema, la soluzione migliore che ho trovato è stata quella di cercare continuamente di ottenere un blocco esclusivo sul file; mentre il file è in fase di scrittura, il tentativo di blocco fallirà, essenzialmente il metodo nella risposta this. Una volta che il file non viene più scritto, il blocco avrà successo.

Sfortunatamente, l'unico modo per fare ciò è avvolgere un tentativo/aggirare l'apertura del file, il che mi fa rabbrividire: dover usare try/catch è sempre doloroso. Non sembra che ci sia un modo per aggirare questo, però, quindi è quello che ho finito per usare.

Modificare il codice in quella risposta fa il trucco, così ho finito per usare qualcosa di simile:

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

Si potrebbe voler aggiungere un conteggio ed uscire provando dopo x tentativi falliti. – McGarnagle

1

Ecco un metodo che ritentare l'accesso ai file fino a un numero X di volte, con un Sleep tra cerca. Se non si ottiene l'accesso, l'applicazione passa:

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; 
} 

Esso può essere utilizzato in questo modo:

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; 

... naturalmente, è possibile modificare gli aspetti di questo per soddisfare il vostro esigenze.

Problemi correlati