【问题标题】:FileSystemWatcher reports file available on network share but file cannot be foundFileSystemWatcher 报告文件在网络共享上可用,但找不到文件
【发布时间】:2026-02-08 10:15:01
【问题描述】:

背景

我的服务器有一个共享文件夹 \\Server\Share 和 4 个子文件夹:

  • OutboundFinal
  • OutboundStaging
  • 入站决赛
  • 入站暂存

所有文件夹都驻留在同一个物理磁盘和分区上,不使用连接点。

我还有几个 WinForms 客户端(最多 10 个)向此共享写入和读取文件,每个客户端都在多个线程上工作(最多 5 个)。文件由客户端(总共最多 50 个线程)写入\\Server\Share\OutboundStaging 文件夹。每个文件都有一个 GUID 的名称,因此不会被覆盖。一旦文件被完全写入,它就会被客户端移动到\\Server\Share\OutboundFinal 文件夹。在同一台服务器上运行的 Windows 服务会将其拾取、删除、处理,然后将同名文件写入\\Server\Share\InboundStaging 文件夹。文件完全写入后,服务将移动到\\Server\Share\InboundFinal 文件夹。

此 \\Server\Share\InboundFinal 文件夹由每个 WinForms 客户端的每个线程使用 FileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed | WatcherChangeTypes.Created, timeOut); 进行监控 FileSystemWatcher.Filter 设置为某个线程希望在 \Server\Share\InboundFinal 文件夹中看到的文件的 GUID 文件名,因此 FileSystemWatcher 会一直等待,直到文件夹中显示特定文件。

我已经阅读了几个关于 FileSystemWatcher 行为异常且未报告 UNC 共享更改的 SO 问题。但对我来说不是这样。

我使用的代码如下所示:

    FileSystemWatcher fileWatcher = new FileSystemWatcher();
    fileWatcher.Path = InboundFinalFolder;
    fileWatcher.Filter = GUIDFileName; // contains full UNC path AND the file name
    fileWatcher.EnableRaisingEvents = true;
    fileWatcher.IncludeSubdirectories = false;
    var res = fileWatcher.WaitForChanged(WatcherChangeTypes.Changed | WatcherChangeTypes.Created, timeOut);
    if (!fileWatcher.TimedOut)
    {
        using (FileStream stream = fi.Open(FileMode.Open, FileAccess.Read, FileShare.Read)) {
        byte[] res = new byte[stream.Length];
        stream.Read(res, 0, stream.Length);
        return res;
    }

这是抛出异常的 using 行。

问题

我假设 fileWatcher.WaitForChanged 只有在具有正确 GUID 名称的文件位于 \\Server\Share\InboundFinal 文件夹中时才会继续。这正是 FileSystemWatcher 在本地文件夹上的工作方式,但不适用于通过网络访问的文件共享(本地文件,即使通过共享访问,也倾向于工作)。 FileSystemWatcher 报告线程正在等待的文件位于 FileSystemWatcher \\Server\Share\InboundFinal 文件夹中。但是,当我尝试读取文件时,我得到了 FileNotFoundException。读取线程必须等待 3-15 秒才能读取文件。我尝试使用 Read 共享的 FileStream 打开文件。

什么可能导致这种行为?我该如何解决它?理想情况下,FileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed | WatcherChangeTypes.Created, timeOut); 应该仅在可以读取文件或发生超时时继续执行。

【问题讨论】:

  • FileNotFoundException 非常基本。一个标准错误是使用 GUIDFileName 打开文件。不够好,它不是一条完整的路径。测试时偶然工作,从不在服务器上工作。也可以在禁用反恶意软件的情况下尝试此操作,以防万一它认为在扫描文件时隐藏文件是个好主意。您没有发布实际失败的代码,所以这些只是猜测。
  • GUIDFileName 属性包含完整的 UNC 路径和 GUID 文件名。抱歉,我在我的问题中编辑了代码 sn-p 以澄清。
  • 抛出异常的是 using 行。

标签: multithreading filesystemwatcher shared-directory windows-share


【解决方案1】:

FileSystemWatcher 名声不好,但其实还不错……

1.)

您的代码示例无法编译。我试过这个:

 FileSystemWatcher fileWatcher = new FileSystemWatcher();
 fileWatcher.Path = "X:\\temp";
 fileWatcher.Filter = "test.txt";
 fileWatcher.EnableRaisingEvents = true;
 fileWatcher.IncludeSubdirectories = false;

 var res = fileWatcher.WaitForChanged(WatcherChangeTypes.Changed |
                                 WatcherChangeTypes.Created, 20000);
 if (!res.TimedOut)
 {
     FileInfo fi = new FileInfo(Path.Combine(fileWatcher.Path, res.Name));

     using (FileStream stream = fi.Open(FileMode.Open, FileAccess.Read, FileShare.Read))
     {
         byte[] buf = new byte[stream.Length];

         stream.Read(buf, 0, (int)stream.Length);
     }

     Console.WriteLine("read ok");
 }
 else
 {
     Console.WriteLine("time out");
 }

我对此进行了测试,其中 X: 是 SMB 共享。它没有问题(对我来说,见下文)。

但是:

您应该重试打开/读取文件(每次打开失败后休眠 100 毫秒)。这是因为您可能会遇到 FileSystemWatcher 检测到文件,但移动(或另一个写入操作)尚未结束的情况,因此您必须等到文件创建/移动器真正准备好。

或者您不等待“真实”文件,而是等待文件移动任务在关闭“真实”文件后创建的标志文件。

2.)

会不会是移动任务没有正确关闭文件?

3.)

几年前,我有一些工具(用 perl 编写),其中一个脚本创建一个标志文件,另一个脚本等待它。

我在 SMB 2 共享上遇到了一些令人讨厌的问题。我发现这是由于 SMB 缓存造成的。

https://bogner.sh/2014/10/how-to-disable-smb-client-side-caching/

File open fails initially when trying to open a file located on a win2k8 share but eventually can succeeed

https://technet.microsoft.com/en-us/library/ff686200.aspx

试试这个(在客户端):

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\LanmanWorkstation\Parameters]

"DirectoryCacheLifetime"=dword:00000000
"FileNotFoundCacheLifetime"=dword:00000000

将此保存为 disablecache.reg 并运行 regedit disablecache.reg

然后重启。

【讨论】:

  • 我不知道为什么它被否决(我已解决),因为您的回答包含大量对我的案例有用的信息。事实上,这个答案有很多与我的问题有关的事情。
  • 感谢您的回答。 FileNotFoundCacheLifetime 设置似乎正是我所需要的。我重试打开文件(10 次重试,延迟 1000 毫秒,因为这对我来说真的没关系)但有时甚至这还不够。我只是省略了那部分代码,因为它似乎无关紧要。