等到文件完全写入

当在一个目录中创build一个文件( FileSystemWatcher_Created )时,我将它复制到另一个目录中。 但是,当我创build一个大(> 10MB)的文件无法复制文件,因为它已经开始复制,当文件尚未完成创build…
这会导致无法复制该文件,因为它被另一个进程使用来引发。 (
任何帮助?

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

您面临的问题只有解决方法。

在开始复制过程之前,检查是否正在处理文件ID。 您可以调用以下函数,直到获得False值。

第一种方法,直接从这个答案复制:

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

第二种方法:

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

FileSystemWatcher的文档:

一旦创build文件,就会引发OnCreated事件。 如果正在将文件复制或传输到监视的目录中,则会立即引发OnCreated事件,然后是一个或多个OnChanged事件。

因此,如果复制失败(捕获exception),将其添加到仍然需要移动的文件列表中,并在OnChanged事件期间尝试复制。 最终,它应该工作。

像(不完整;捕获特定的exception,初始化variables等):

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

这是一个古老的线程,但我会为其他人添加一些信息。

我遇到了一个类似的问题,编写PDF文件的程序,有时他们需要30秒渲染..这是我的watcher_FileCreated类在复制文件之前等待的同一时期。

这些文件没有被locking。

在这种情况下,我检查了PDF的大小,然后等待2秒钟,然后再比较新的大小,如果它们不相等,线程将睡30秒,然后再试一次。

你真的很幸运 – 编写文件的程序locking它,所以你不能打开它。 如果没有locking它,你就会复制一个部分文件,而不知道有什么问题。

当你不能访问一个文件时,你可以假设它仍然在使用中(更好的方法是尝试以独占模式打开它,看看别人是否正在打开它,而不是从File.Copy的失败中猜测)。 如果该文件被locking,则必须在其他时间复制该文件。 如果没有locking,可以复制它(这里有一个竞争条件的可能性很小)。

那个'其他时间'是什么时候? 我不记得FileSystemWatcher每个文件发送多个事件的时间 – 检查一下,只要忽略事件并等待另一个事件就足够了。 如果没有,您可以随时设置一个时间,并在5秒内重新检查文件。

那么你已经自己给出了答案。 您必须等待文件的创build完成。 一种方法是通过检查文件是否仍在使用中。 这样的例子可以在这里find: 有没有办法来检查一个文件是否正在使用?

请注意,您将不得不修改此代码才能在您的情况下工作。 你可能想要像(伪代码):

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

显然你应该保护自己无限的while以防万一所有者的应用程序永远不会释放锁。 另外,从FileSystemWatcher可以订阅其他事件也许是值得的。 可能有一个事件可以用来规避这个问题。

所以,通过这些以及其他类似的问题,我迅速地浏览了一下,我今天下午进行了一个快乐的鹅追逐,试图用两个单独的程序来解决一个问题,使用一个文件作为同步(也是文件保存)方法。 有点不寻常的情况,但它肯定突出了“检查文件是否locking,然后打开它,如果不是”方法的问题。

问题是:在检查文件和实际打开文件的时间之间,文件可能会locking。 它很难追踪零星无法复制该文件,因为如果你不是也在寻找它,它被另一个进程错误使用

基本的解决方法是试图打开一个catch块内的文件,这样如果它被locking,你可以再试一次。 这样,检查和开放之间没有经过的时间,操作系统在同一时间。

这里的代码使用File.Copy,但是它与File类的任何静态方法一样工作:File.Open,File.ReadAllText,File.WriteAllText等

 /// <param name="timeout">how long to keep trying in milliseconds</param> 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; } } 

关于相似性的另一个小logging:这是一个同步方法,它会在等待和处理线程时阻塞它的线程。 这是最简单的方法,但如果文件长时间处于locking状态,程序可能无法响应。 在这里,父母主义是一个太深入的话题(你可以设置asynchronous读/写的方式有点荒谬),但这里有一种可以相互混淆的方式。

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

这里是如何使用它:

 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! 

您可以使用下面的代码来检查文件是否可以独占访问打开(即,它不是由另一个应用程序打开)。 如果文件没有closures,您可以稍等片刻,再次检查,直到文件closures,您可以安全地复制它。

您仍然应该检查File.Copy是否失败,因为另一个应用程序可能会在您检查文件和复制文件之间打开文件。

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

当文件以二进制(逐字节)写入时,创buildFileStream和上面的解决scheme不起作用,因为文件已经准备就绪,并且在每个字节中都有提示,所以在这种情况下,您需要其他解决方法,例如:创build文件或者想要时开始处理文件

 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! 

我想在这里添加一个答案,因为这对我有用。 我用了时间延迟,while循环,所有我能想到的。

我打开了输出文件夹的Windows资源pipe理器窗口。 我把它关了,一切都像魅力一样。

我希望这可以帮助别人。