当多个文件放入监视目录时,我遇到了FileSystemWatcher的问题.我想在文件放入目录后立即解析它.通常,第一个文件解析正常,但向目录添加第二个文件会导致访问问题.偶尔,第一个文件甚至不会解析.只有一个应用程序正在运行并正在查看此目录.最终,此进程将在多台计算机上运行,并且它们将监视共享目录,但只有一台服务器可以解析每个文件,因为数据已导入数据库且没有主键.
这是FileSystemWatcher代码:
public void Run() { FileSystemWatcher watcher = new FileSystemWatcher("C:\\temp"); watcher.NotifyFilter = NotifyFilters.FileName; watcher.Filter = "*.txt"; watcher.Created += new FileSystemEventHandler(OnChanged); watcher.EnableRaisingEvents = true; System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite); }
然后是解析文件的方法:
private void OnChanged(object source, FileSystemEventArgs e) { string line = null; try { using (FileStream fs = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.None)) { using (StreamReader sr = new StreamReader(fs)) { while (sr.EndOfStream == false) { line = sr.ReadLine(); //parse the line and insert into the database } } } } catch (IOException ioe) { Console.WriteLine("OnChanged: Caught Exception reading file [{0}]", ioe.ToString()); }
移动第二个文件时,它正在捕捉
System.IO.IOException:进程无法访问文件'C:\ Temp\TestFile.txt',因为它正由另一个进程使用.
如果它在多台机器上运行,我希望看到这个错误,但它现在只在一台服务器上运行.不应该有另一个使用此文件的进程 - 我创建它们并在应用程序运行时将它们复制到目录中.
这是设置FileSystemWatcher的正确方法吗?如何查看此文件的锁定?为什么不解析这两个文件 - 我是否必须关闭FileStream?我想保留FileShare.None选项,因为我只想要一个服务器来解析文件 - 获取文件的服务器首先解析它.
此方法的典型问题是在触发事件时仍在复制文件.显然,您将获得异常,因为文件在复制期间被锁定.大文件特别容易出现异常.
作为一种解决方法,您可以先复制该文件,然后重命名该文件并收听重命名事件.
或者另一种选择是使用while循环检查是否可以使用写访问权打开文件.如果可以,您将知道复制已完成.C#代码可能如下所示(在生产系统中,您可能希望具有最大重试次数或超时而不是a while(true)
):
////// Waits until a file can be opened with write permission /// public static void WaitReady(string fileName) { while (true) { try { using (Stream stream = System.IO.File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { if (stream != null) { System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName)); break; } } } catch (FileNotFoundException ex) { System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message)); } catch (IOException ex) { System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message)); } catch (UnauthorizedAccessException ex) { System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} not yet ready ({1})", fileName, ex.Message)); } Thread.Sleep(500); } }
另一种方法是在复制完成后在文件夹中放置一个小的触发器文件.您的FileSystemWatcher只会侦听触发器文件.
我上面已经发表评论,但我还没有足够的分数.
对这个问题的评价最高的答案有一段看起来像这样的代码:
using (Stream stream = System.IO.File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) { if (stream != null) { System.Diagnostics.Trace.WriteLine(string.Format("Output file {0} ready.", fileName)); break; } }
使用FileShare.ReadWrite
设置的问题在于它正在请求访问该文件,主要是说"我想读/写这个文件,但其他人也可以读/写它".这种方法在我们的情况下失败了 接收远程传输的进程没有锁定文件,但它正在积极地写入它.我们的下游代码(SharpZipLib)因"正在使用文件"异常而失败,因为它试图打开文件FileShare.Read
("我想要读取文件,只允许其他进程读取").由于打开文件的进程已经写入,因此该请求失败.
但是,上面的响应中的代码太宽松了.通过使用FileShare.ReadWrite
,它成功获得了对文件的访问权限(因为它要求可以兑现的共享限制),但下游调用仍然失败.
调用中的共享设置File.Open
应为FileShare.Read
或FileShare.None
,而不是 FileShare.ReadWrite
.