Файл отчетов FileSystemWatcher доступен на общем сетевом ресурсе, но файл не найден

ФОН

У меня есть сервер с общей папкой \\Server\Share с 4 подпапками:

  • OutboundFinal
  • OutboundStaging
  • InboundFinal
  • 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 устанавливается в имя файла GUID файла, который определенный поток ожидает увидеть в папке \ Server \ Share \ InboundFinal, поэтому FileSystemWatcher ожидает, пока в папке не отобразится определенный файл.

Я прочитал несколько вопросов о FileSystemWatcher ведет себя хаотично и не сообщает об изменениях в акциях UNC. Однако это не так для меня.

Код, который я использую, выглядит следующим образом:

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

Это строка с использованием исключения.

ЭТА ПРОБЛЕМА

Я хотел бы предположить, что fileWatcher.WaitForChanged будет продолжаться, только если файл с правильным именем GUID находится в \\Server\Share\InboundFinal папка. Именно так FileSystemWatcher работает с локальными папками, но не с общими файловыми ресурсами, доступ к которым осуществляется через сеть (локальные файлы, даже доступные через общий ресурс, также обычно работают). FileSystemWatcher сообщает, что файл, который ожидает поток, находится в FileSystemWatcher \\Server\Share\InboundFinal папка. Однако когда я пытаюсь прочитать файл, я получаю исключение FileNotFoundException. Поток чтения должен ждать 3-15 секунд, прежде чем файл может быть прочитан. Я пытаюсь открыть файл с FileStream с Read обмен.

Что может вызвать такое поведение? Как мне обойти это? В идеале FileSystemWatcher.WaitForChanged(WatcherChangeTypes.Changed | WatcherChangeTypes.Created, timeOut); следует продолжить выполнение, только если файл может быть прочитан или истекло время ожидания.

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/

При открытии файла, находящегося на общем ресурсе win2k8, сначала происходит сбой

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

Затем перезагрузите компьютер.

Другие вопросы по тегам