FileSystemWatcher 报告文件在网络共享上可用但找不到文件
FileSystemWatcher reports file available on network share but file cannot be found
背景
我的服务器有一个共享文件夹 \Server\Share
和 4 个子文件夹:
- 最终出站
- 出站暂存
- 最终入站
- InboundStaging
所有文件夹都位于同一个物理磁盘和分区上,没有使用连接点。
我还有几个 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);
应该只在文件可以读取或发生超时时继续执行。
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 检测到文件但移动(或另一个写入操作)尚未结束的情况,因此您必须等到文件创建/移动器真正准备就绪。
或者您不等待 "real" 文件,而是等待文件移动任务在关闭 "real" 文件后创建的标志文件。
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
然后重启。
背景
我的服务器有一个共享文件夹 \Server\Share
和 4 个子文件夹:
- 最终出站
- 出站暂存
- 最终入站
- InboundStaging
所有文件夹都位于同一个物理磁盘和分区上,没有使用连接点。
我还有几个 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);
应该只在文件可以读取或发生超时时继续执行。
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 检测到文件但移动(或另一个写入操作)尚未结束的情况,因此您必须等到文件创建/移动器真正准备就绪。
或者您不等待 "real" 文件,而是等待文件移动任务在关闭 "real" 文件后创建的标志文件。
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
然后重启。