Мониторинг FTP-каталога в ASP.NET/C# - PullRequest
1 голос
/ 05 марта 2019

У меня есть FileSystem наблюдатель для локального каталога. Работает нормально. Я хочу это же реализовать для FTP. Есть ли способ, которым я могу этого достичь? Я проверил много решений, но это не ясно.

Логика: хотите получить файлы с FTP позже, чем отметка времени.
Возникла проблема: получение всех файлов с FTP и последующая фильтрация результатов снижают производительность (используется FtpWebRequest).

Есть ли правильный способ сделать это? (WinSCP приостановлен. Не могу использовать его сейчас.)

FileSystemWatcher oFsWatcher = new FileSystemWatcher();
OFSWatchers.Add(oFsWatcher);
oFsWatcher.Path = sFilePath;

oFsWatcher.Filter = string.IsNullOrWhiteSpace(sFileFilter) ? "*.*" : sFileFilter;
oFsWatcher.NotifyFilter = NotifyFilters.FileName;

oFsWatcher.EnableRaisingEvents = true;
oFsWatcher.IncludeSubdirectories = bIncludeSubdirectories;
oFsWatcher.Created += new FileSystemEventHandler(OFsWatcher_Created);

Ответы [ 3 ]

1 голос
/ 05 марта 2019

Вы не можете использовать FileSystemWatcher или любой другой способ, потому что у протокола FTP нет API для уведомления клиента об изменениях в удаленном каталоге.

Все, что вы можетеДля этого периодически нужно выполнять итерации удаленного дерева и находить изменения.

На самом деле это довольно просто реализовать, если вы используете клиентскую библиотеку FTP, которая поддерживает рекурсивный список удаленного дерева.К сожалению, встроенный FTP-клиент .NET FtpWebRequest этого не делает.Но, например, с WinSCP .NET сборка , вы можете использовать Session.EnumerateRemoteFiles метод .

См. Статью Отслеживание изменений в SFTP / FTP-сервере:

// Setup session options
SessionOptions sessionOptions = new SessionOptions
{
    Protocol = Protocol.Ftp,
    HostName = "example.com",
    UserName = "user",
    Password = "password",
};

using (Session session = new Session())
{
    // Connect
    session.Open(sessionOptions);

    List<string> prevFiles = null;

    while (true)
    {
        // Collect file list
        List<string> files =
            session.EnumerateRemoteFiles(
                "/remote/path", "*.*", EnumerationOptions.AllDirectories)
            .Select(fileInfo => fileInfo.FullName)
            .ToList();
        if (prevFiles == null)
        {
            // In the first round, just print number of files found
            Console.WriteLine("Found {0} files", files.Count);
        }
        else
        {
            // Then look for differences against the previous list
            IEnumerable<string> added = files.Except(prevFiles);
            if (added.Any())
            {
                Console.WriteLine("Added files:");
                foreach (string path in added)
                {
                    Console.WriteLine(path);
                }
            }

            IEnumerable<string> removed = prevFiles.Except(files);
            if (removed.Any())
            {
                Console.WriteLine("Removed files:");
                foreach (string path in removed)
                {
                    Console.WriteLine(path);
                }
            }
        }

        prevFiles = files;

        Console.WriteLine("Sleeping 10s...");
        Thread.Sleep(10000);
    }
}

(я автор WinSCP)


Хотя, если вы действительно хотите просто загрузить изменения, этонамного проще.Просто используйте Session.SynchronizeDirectories в цикле.

while (true)
{
    SynchronizationResult result =
        session.SynchronizeDirectories(
            SynchronizationMode.Local, "/remote/path", @"C:\local\path", true);
    result.Check();
    // You can inspect result.Downloads for a list for updated files

    Console.WriteLine("Sleeping 10s...");
    Thread.Sleep(10000);
}

Это обновит даже измененные файлы, а не только новые файлы.


Несмотря на использование WinSCP.NET сборка из веб-приложения может быть проблематичной.Если вы не хотите использовать стороннюю библиотеку, вам нужно ограничиться FtpWebRequest.Пример рекурсивного вывода списка удаленных каталогов с помощью FtpWebRequest см. В моем ответе на Список имен файлов в каталоге FTP и его подкаталогах .


Вы редактировалиВаш вопрос, чтобы сказать, что у вас есть проблемы с производительностью с решениями, которые я предложил.Хотя вы уже задали новый вопрос, который охватывает это:
Получить сведения о файле FTP на основе даты и времени в C #

1 голос
/ 05 марта 2019

Если у вас нет доступа к ОС, в которой размещается служба;это будет немного сложнее.

FileSystemWatcher устанавливает хук на файловую систему, которая уведомит ваше приложение, как только что-то произойдет.

Спецификации команд FTP такого крючка не имеет.Кроме того, он всегда инициируется клиентом.

Поэтому для реализации такой логики вы должны периодически выполнять NLST для вывода списка содержимого каталога FTP и отслеживания изменений (или, возможно, хэшей (MDTM)).) себя.

Дополнительная информация:

0 голосов
/ 15 марта 2019

У меня есть альтернативное решение для выполнения моих функций.

Объяснение:

Я загружаю файлы с FTP (разрешение на чтениеТребуется.) с той же структурой папок.

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

Это просто альтернативное решение.

Изменения кода:

 private static void GetFiles()
 {
    using (FtpClient conn = new FtpClient())
    {
        string ftpPath = "ftp://myftp/";

        string downloadFileName = @"C:\temp\FTPTest\";

        downloadFileName +=  "\\";

        conn.Host = ftpPath;
        //conn.Credentials = new NetworkCredential("ftptest", "ftptest");
        conn.Connect();

        //Get all directories

        foreach (FtpListItem item in conn.GetListing(conn.GetWorkingDirectory(),
            FtpListOption.Modify | FtpListOption.Recursive))
        {
            // if this is a file
            if (item.Type == FtpFileSystemObjectType.File)
            {
                string localFilePath = downloadFileName + item.FullName;

                //Only newly created files will be downloaded.
                if (!File.Exists(localFilePath))
                {
                    conn.DownloadFile(localFilePath, item.FullName);
                    //Do any action here.
                    Console.WriteLine(item.FullName);
                }
            }
        }
    }
}
...