Мониторинг файлов: как узнать, что файл завершен

StackOverflow https://stackoverflow.com/questions/30074

  •  09-06-2019
  •  | 
  •  

Вопрос

У нас есть несколько приложений .NET, которые отслеживают наличие новых файлов в каталоге с помощью FileSystemWatcher.Файлы копируются из другого места, загружаются по FTP и т. д.Когда они поступают, файлы тем или иным образом обрабатываются.Однако есть одна проблема, на которую я никогда не видел удовлетворительного ответа:для больших файлов как узнать, что в отслеживаемые файлы все еще ведется запись?Очевидно, нам нужно дождаться, пока файлы будут завершены и закрыты, прежде чем мы начнем их обрабатывать.Аргументы событий в событиях FileSystemWatcher, похоже, не решают эту проблему.

Это было полезно?

Решение

Вы пытались получить блокировку записи в файл?Если в него записывается, это должно потерпеть неудачу, и вы знаете, что нужно оставить его в покое на некоторое время...

Другие советы

Если вы контролируете программу, которая записывает файлы в каталог, вы можете заставить программу записать файлы во временный каталог, а затем переместить их в наблюдаемый каталог.Перемещение должно быть атомарной операцией, поэтому наблюдатель не должен видеть файл, пока он полностью не окажется в каталоге.

Если вы не контролируете, что записывается в отслеживаемый каталог, вы можете установить в наблюдателе время, когда файл считается завершенным, если его размер остается прежним в течение заданного времени.Если немедленная обработка не является проблемой, установка этого таймера на что-то относительно большое - это довольно безопасный способ узнать, что файл либо завершен, либо никогда не будет.

Событие «Изменено» в FileSystemWatcher не должно срабатывать до тех пор, пока файл не будет закрыт.Смотри мой ответ на аналогичный вопрос.Существует вероятность того, что механизм загрузки FTP закроет файл несколько раз во время загрузки по мере поступления новых данных, но я думаю, что это маловероятно.

Если содержимое файла не может быть проверено на завершение (он имеет проверяемый формат или включает контрольную сумму содержимого), только отправитель может проверить, что получен весь файл.

Раньше я использовал метод блокировки для отправки больших файлов через FTP.

Файл отправляется с альтернативным расширением и переименовывается, как только отправитель убеждается, что все есть.

Вышеупомянутое, очевидно, сочетается с процессом, который периодически очищает старые файлы с временным расширением.

Альтернативой является создание файла нулевой длины с тем же именем, но с дополнительным расширением .lck.Как только настоящий файл будет полностью загружен, файл lck будет удален.Получающий процесс, очевидно, игнорирует файлы, имеющие имя файла блокировки.

Без такой системы получатель никогда не сможет быть уверен, что весь файл доставлен.

Проверка файлов, которые не были изменены за x минут, может привести к разного рода проблемам.

Следующий метод пытается открыть файл с разрешениями на запись.Он будет блокировать выполнение до тех пор, пока файл не будет полностью записан на диск:

/// <summary>
/// Waits until a file can be opened with write permission
/// </summary>
public static void WaitReady(string fileName)
{
    while (true)
    {
        try
        {
            using (System.IO.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);
    }
}

(из моего ответа на связанный вопрос)

Вероятно, вам придется использовать внеполосную сигнализацию:пусть производитель "file.ext" напишет фиктивный "file.ext.end".

+1 за использование сигнализатора file.ext.end, если это возможно, где содержимое file.ext.end представляет собой контрольную сумму для большего файла.Это сделано не столько для безопасности, сколько для того, чтобы ничего не было искажено по пути.Если кто-то может вставить свой собственный файл в большой поток, он также может заменить контрольную сумму.

Блокировка записи не помогает, если загрузка файла не удалась на полпути и отправитель еще не попытался повторно отправить (и повторно заблокировать) файл.

Я проверяю в Windows, был ли файл полностью загружен по FTP, пытаясь переименовать его.Если переименование не удалось, файл не завершен.Признаю, не очень элегантно, но работает.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top