Aguarde até que o arquivo esteja completamente escrito

Quando um arquivo é criado ( FileSystemWatcher_Created ) em um diretório, copio-o para outro. Mas quando eu crio um arquivo grande (> 10MB), ele não consegue copiar o arquivo, porque ele já começa a copiar, quando o arquivo ainda não terminou de criar …
Isso faz com que Não é possível copiar o arquivo, porque ele é usado por outro processo a ser gerado. ; (
Qualquer ajuda?

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

Há apenas uma solução alternativa para o problema que você está enfrentando.

Verifique se o ID do arquivo está em processo antes de iniciar o processo de cópia. Você pode chamar a seguinte function até obter o valor Falso.

1º Método, copiado diretamente desta resposta :

 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º método:

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

Na documentação do FileSystemWatcher :

O evento OnCreated é gerado assim que um arquivo é criado. Se um arquivo estiver sendo copiado ou transferido para um diretório monitorado, o evento OnCreated será gerado imediatamente, seguido por um ou mais events OnChanged .

Portanto, se a cópia falhar (capture a exceção), adicione-a a uma lista de arquivos que ainda precisam ser movidos e tente a cópia durante o evento OnChanged . Eventualmente, deve funcionar.

Algo como (incompleto; pegar exceções específicas, inicializar variables, etc):

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

É um tópico antigo, mas adicionarei algumas informações para outras pessoas.

Eu experimentei um problema semelhante com um programa que grava arquivos PDF, às vezes eles levam 30 segundos para renderizar .. que é o mesmo período que minha class watcher_FileCreated aguarda antes de copiar o arquivo.

Os arquivos não foram bloqueados.

Nesse caso, verifiquei o tamanho do PDF e, em seguida, esperei dois segundos antes de comparar o novo tamanho, se eles estivessem desiguais, o encadeamento iria dormir por 30 segundos e tente novamente.

Você está realmente com sorte – o programa que está escrevendo o arquivo bloqueia, então você não pode abri-lo. Se não tivesse bloqueado, você teria copiado um arquivo parcial, sem ter qualquer ideia de que há um problema.

Quando você não pode acessar um arquivo, você pode assumir que ele ainda está em uso (melhor ainda – tente abri-lo no modo exclusivo e ver se alguém está abrindo o arquivo, em vez de adivinhá-lo pela falha do File.Copy). Se o arquivo estiver bloqueado, você terá que copiá-lo em algum outro momento. Se não estiver bloqueado, você pode copiá-lo (há um pequeno potencial para uma condição de corrida aqui).

Quando é essa “outra vez”? Eu não lembro quando FileSystemWatcher envia vários events por arquivo – confira, pode ser o suficiente para você simplesmente ignorar o evento e esperar por outro. Se não, você pode sempre configurar um tempo e verificar novamente o arquivo em 5 segundos.

Bem, você já deu a resposta a si mesmo; você tem que esperar pela criação do arquivo para terminar. Uma maneira de fazer isso é verificar se o arquivo ainda está em uso. Um exemplo disso pode ser encontrado aqui: Existe uma maneira de verificar se um arquivo está em uso?

Note que você terá que modificar este código para que funcione na sua situação. Você pode querer ter algo como (pseudocódigo):

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

Obviamente, você deve se proteger de um infinito while apenas no caso do aplicativo proprietário nunca liberar o bloqueio. Além disso, pode valer a pena conferir os outros events do FileSystemWatcher você pode se inscrever. Pode haver um evento que você pode usar para contornar esse problema todo.

Então, tendo olhado rapidamente através de algumas dessas e outras questões semelhantes, eu fui em uma alegre busca nesta tarde tentando resolver um problema com dois programas separados usando um arquivo como um método de synchronization (e também salvar arquivos). Um pouco de uma situação incomum, mas definitivamente destacou para mim os problemas com o ‘verifique se o arquivo está bloqueado, em seguida, abra-o se não é’ abordagem.

O problema é o seguinte: o arquivo pode ficar bloqueado entre a hora em que você o verifica e a hora em que você realmente abre o arquivo. É realmente difícil rastrear o esporádico Não é possível copiar o arquivo, porque ele é usado por outro erro de processo se você não estiver procurando por ele também.

A resolução básica é apenas tentar abrir o arquivo dentro de um bloco catch para que, se estiver bloqueado, você possa tentar novamente. Dessa forma não há tempo decorrido entre o cheque e a abertura, o sistema operacional os faz ao mesmo tempo.

O código aqui usa File.Copy, mas funciona tão bem com qualquer um dos methods estáticos da class File: File.Open, File.ReadAllText, File.WriteAllText, etc.

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

Outra pequena nota sobre o parelelismo: Este é um método síncrono, que irá bloquear seu thread enquanto espera e enquanto trabalha no thread. Essa é a abordagem mais simples, mas se o arquivo permanecer bloqueado por muito tempo, seu programa poderá ficar sem resposta. O parelelismo é um tópico muito grande para ser aprofundado aqui (e o número de maneiras pelas quais você poderia configurar leitura / gravação assíncrona é meio que absurdo), mas aqui está uma forma de parelelizar.

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

E aqui está como isso poderia ser usado:

 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! 

Você pode usar o seguinte código para verificar se o arquivo pode ser aberto com access exclusivo (isto é, não é aberto por outro aplicativo). Se o arquivo não estiver fechado, aguarde alguns instantes e verifique novamente até que o arquivo seja fechado e você possa copiá-lo com segurança.

Você ainda deve verificar se o File.Copy falha, porque outro aplicativo pode abrir o arquivo entre o momento em que você verifica o arquivo e o momento em que o copia.

 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 o arquivo está escrevendo em binário (byte by byte), crie FileStream e acima de soluções Não está funcionando, porque o arquivo está pronto e encadeado em todos os bytes, portanto, nesta situação você precisa de outra solução alternativa como esta: Faça isto quando o arquivo for criado ou você quiser para iniciar o processamento no arquivo

 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! 

Eu gostaria de acrescentar uma resposta aqui, porque isso funcionou para mim. Eu usei atrasos de tempo, enquanto loops, tudo que eu conseguia pensar.

Eu tinha a janela do Windows Explorer da pasta de saída aberta. Fechei e tudo funcionou como um encanto.

Espero que isso ajude alguém.