Attendere fino a quando il file è completamente scritto

Quando un file viene creato ( FileSystemWatcher_Created ) in una directory, lo copio su un altro. Ma quando creo un file grande (> 10 MB) non riesce a copiare il file, perché inizia già a copiare, quando il file non ha ancora finito di creare …
Ciò causa Imansible copiare il file, perché è utilizzato da un altro processo per essere sollevato. ; (
Qualsiasi aiuto?

 class Program { static void Main(string[] args) { string path = @"D:\levan\FolderListenerTest\ListenedFolder"; FileSystemWatcher listener; listener = new FileSystemWatcher(path); listener.Created += new FileSystemEventHandler(listener_Created); listener.EnableRaisingEvents = true; while (Console.ReadLine() != "exit") ; } public static void listener_Created(object sender, FileSystemEventArgs e) { Console.WriteLine ( "File Created:\n" + "ChangeType: " + e.ChangeType + "\nName: " + e.Name + "\nFullPath: " + e.FullPath ); File.Copy(e.FullPath, @"D:\levan\FolderListenerTest\CopiedFilesFolder\" + e.Name); Console.Read(); } } 

C’è solo una soluzione alternativa per il problema che stai affrontando.

Verifica se l’ID file è in elaborazione prima di iniziare il processo di copia. È ansible chiamare la seguente funzione finché non si ottiene il valore False.

1o metodo, copiato direttamente da questa risposta :

 private bool IsFileLocked(FileInfo file) { FileStream stream = null; try { stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None); } catch (IOException) { //the file is unavailable because it is: //still being written to //or being processed by another thread //or does not exist (has already been processed) return true; } finally { if (stream != null) stream.Close(); } //file is not locked return false; } 

2 ° metodo:

 const int ERROR_SHARING_VIOLATION = 32; const int ERROR_LOCK_VIOLATION = 33; private bool IsFileLocked(string file) { //check that problem is not in destination file if (File.Exists(file) == true) { FileStream stream = null; try { stream = File.Open(file, FileMode.Open, FileAccess.ReadWrite, FileShare.None); } catch (Exception ex2) { //_log.WriteLog(ex2, "Error in checking whether file is locked " + file); int errorCode = Marshal.GetHRForException(ex2) & ((1 < < 16) - 1); if ((ex2 is IOException) && (errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION)) { return true; } } finally { if (stream != null) stream.Close(); } } return false; } 

Dalla documentazione di FileSystemWatcher :

L’evento OnCreated viene generato non appena viene creato un file. Se un file viene copiato o trasferito in una directory controllata, l’evento OnCreated verrà sollevato immediatamente, seguito da uno o più eventi OnChanged .

Quindi, se la copia fallisce, (prendi l’eccezione), aggiungila a un elenco di file che devono ancora essere spostati, e prova la copia durante l’evento OnChanged . Alla fine, dovrebbe funzionare.

Qualcosa di simile (incompleto, cattura eccezioni specifiche, inizializza variabili, ecc):

  public static void listener_Created(object sender, FileSystemEventArgs e) { Console.WriteLine ( "File Created:\n" + "ChangeType: " + e.ChangeType + "\nName: " + e.Name + "\nFullPath: " + e.FullPath ); try { File.Copy(e.FullPath, @"D:\levani\FolderListenerTest\CopiedFilesFolder\" + e.Name); } catch { _waitingForClose.Add(e.FullPath); } Console.Read(); } public static void listener_Changed(object sender, FileSystemEventArgs e) { if (_waitingForClose.Contains(e.FullPath)) { try { File.Copy(...); _waitingForClose.Remove(e.FullPath); } catch {} } } 

È un thread vecchio, ma aggiungerò alcune informazioni per altre persone.

Ho riscontrato un problema simile con un programma che scrive file PDF, a volte richiedono 30 secondi per il rendering .. che è lo stesso periodo in cui la mia class watcher_FileCreated attende prima di copiare il file.

I file non erano bloccati.

In questo caso ho controllato la dimensione del PDF e poi ho aspettato 2 secondi prima di confrontare la nuova dimensione, se non erano uguali il thread avrebbe dormito per 30 secondi e riprovare.

In realtà sei fortunato: il programma che scrive il file lo blocca, quindi non puoi aprirlo. Se non lo avessi bloccato, avresti copiato un file parziale, senza avere alcuna idea che ci fosse un problema.

Quando non riesci ad accedere a un file, puoi presumere che sia ancora in uso (meglio ancora – prova ad aprirlo in modalità esclusiva, e vedere se qualcun altro lo sta attualmente aprendo, invece di indovinare l’errore di File.Copy). Se il file è bloccato, dovrai copiarlo in un altro momento. Se non è bloccato, puoi copiarlo (c’è un leggero potenziale per una condizione di gara qui).

Quando è quella “altra volta”? Non ricordo quando FileSystemWatcher invia più eventi per file: verificarlo, potrebbe essere sufficiente ignorare semplicemente l’evento e attendere un altro. In caso contrario, è sempre ansible impostare un’ora e ricontrollare il file in 5 secondi.

Bene, già dai la tua risposta; devi aspettare che la creazione del file finisca. Un modo per farlo è controllare se il file è ancora in uso. Un esempio di questo può essere trovato qui: C’è un modo per verificare se un file è in uso?

Nota che dovrai modificare questo codice affinché funzioni nella tua situazione. Potresti volere qualcosa come (pseudocodice):

 public static void listener_Created() { while CheckFileInUse() wait 1000 milliseconds CopyFile() } 

Ovviamente dovresti proteggerti da un infinito while nel caso in cui l’applicazione del proprietario non rilascia mai il blocco. Inoltre, potrebbe essere utile verificare gli altri eventi da FileSystemWatcher è ansible abbonarsi. Potrebbe esserci un evento che puoi utilizzare per aggirare l’intero problema.

Quindi, dopo aver dato un’occhiata veloce ad alcune di queste e ad altre domande simili, questo pomeriggio sono andato a caccia di un allegro tentativo di risolvere un problema con due programmi separati che utilizzavano un file come metodo di sincronizzazione (e anche di salvataggio dei file). Un po ‘una situazione insolita, ma ha sicuramente messo in evidenza per me i problemi con il controllo “controlla se il file è bloccato, quindi aprilo se non è”.

Il problema è questo: il file può diventare bloccato tra il momento in cui lo si controlla e la volta in cui si apre effettivamente il file. È davvero difficile rintracciare lo sporadico Imansible copiare il file, perché è utilizzato da un altro errore di processo se non lo si sta cercando.

La risoluzione di base è semplicemente provare ad aprire il file all’interno di un blocco catch in modo che se è bloccato, puoi provare di nuovo. In questo modo non c’è tempo tra il controllo e l’apertura, il sistema operativo li fa allo stesso tempo.

Il codice qui utilizza File.Copy, ma funziona altrettanto bene con qualsiasi metodo statico della class File: File.Open, File.ReadAllText, File.WriteAllText, ecc.

 /// how long to keep trying in milliseconds static void safeCopy(string src, string dst, int timeout) { while (timeout > 0) { try { File.Copy(src, dst); //don't forget to either return from the function or break out fo the while loop break; } catch (IOException) { //you could do the sleep in here, but its probably a good idea to exit the error handler as soon as possible } Thread.Sleep(100); //if its a very long wait this will acumulate very small errors. //For most things it's probably fine, but if you need precision over a long time span, consider // using some sort of timer or DateTime.Now as a better alternative timeout -= 100; } } 

Un’altra piccola nota sul parellelismo: questo è un metodo sincrono, che bloccherà il suo thread sia mentre attende che mentre lavora sul thread. Questo è l’approccio più semplice, ma se il file rimane bloccato per un lungo periodo di tempo il tuo programma potrebbe non rispondere. Il parellelismo è un argomento troppo vasto per approfondire qui (e il numero di modi in cui è ansible impostare la lettura / scrittura asincrona è un po ‘assurdo), ma qui è un modo in cui potrebbe essere parellelizzato.

 public class FileEx { public static async void CopyWaitAsync(string src, string dst, int timeout, Action doWhenDone) { while (timeout > 0) { try { File.Copy(src, dst); doWhenDone(); break; } catch (IOException) { } await Task.Delay(100); timeout -= 100; } } public static async Task ReadAllTextWaitAsync(string filePath, int timeout) { while (timeout > 0) { try { return File.ReadAllText(filePath); } catch (IOException) { } await Task.Delay(100); timeout -= 100; } return ""; } public static async void WriteAllTextWaitAsync(string filePath, string contents, int timeout) { while (timeout > 0) { try { File.WriteAllText(filePath, contents); return; } catch (IOException) { } await Task.Delay(100); timeout -= 100; } } } 

Ed ecco come potrebbe essere usato:

 public static void Main() { test_FileEx(); Console.WriteLine("Me First!"); } public static async void test_FileEx() { await Task.Delay(1); //you can do this, but it gives a compiler warning because it can potentially return immediately without finishing the copy //As a side note, if the file is not locked this will not return until the copy operation completes. Async functions run synchronously //until the first 'await'. See the documentation for async: https://msdn.microsoft.com/en-us/library/hh156513.aspx CopyWaitAsync("file1.txt", "file1.bat", 1000); //this is the normal way of using this kind of async function. Execution of the following lines will always occur AFTER the copy finishes await CopyWaitAsync("file1.txt", "file1.readme", 1000); Console.WriteLine("file1.txt copied to file1.readme"); //The following line doesn't cause a compiler error, but it doesn't make any sense either. ReadAllTextWaitAsync("file1.readme", 1000); //To get the return value of the function, you have to use this function with the await keyword string text = await ReadAllTextWaitAsync("file1.readme", 1000); Console.WriteLine("file1.readme says: " + text); } //Output: //Me First! //file1.txt copied to file1.readme //file1.readme says: Text to be duplicated! 

È ansible utilizzare il codice seguente per verificare se il file può essere aperto con accesso esclusivo (ovvero, non è aperto da un’altra applicazione). Se il file non è chiuso, è ansible attendere alcuni istanti e ricontrollare finché il file non viene chiuso e si può tranquillamente copiarlo.

Dovresti comunque controllare se File.Copy fallisce, perché un’altra applicazione potrebbe aprire il file tra il momento in cui controlli il file e il momento in cui lo copi.

 public static bool IsFileClosed(string filename) { try { using (var inputStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.None)) { return true; } } catch (IOException) { return false; } } 

Quando il file sta scrivendo in binario (byte per byte), crea FileStream e sopra soluzioni Non funziona, perché il file è pronto e scritto in ogni byte, quindi in questa situazione hai bisogno di altre soluzioni come questa: Fai questo quando il file è creato o vuoi per iniziare l’elaborazione su file

 long fileSize = 0; currentFile = new FileInfo(path); while (fileSize < currentFile.Length)//check size is stable or increased { fileSize = currentFile.Length;//get current size System.Threading.Thread.Sleep(500);//wait a moment for processing copy currentFile.Refresh();//refresh length value } //Now file is ready for any process! 

Vorrei aggiungere una risposta qui, perché questo ha funzionato per me. Ho usato ritardi temporali, mentre loop, tutto ciò a cui riuscivo a pensare.

Ho avuto la finestra di Windows Explorer della cartella di output aperta. L’ho chiuso e tutto ha funzionato come un incantesimo.

Spero che questo aiuti qualcuno.