FileSystemWatcher для FTP

Как я могу реализовать FileSystemWatcher для расположения FTP (в C#). Идея в том, что всякий раз, когда что-то добавляется в FTP-местоположение, я хочу скопировать его на свою локальную машину. Любые идеи будут полезны.

Это продолжение моего предыдущего вопроса выборочная загрузка FTP с помощью .NET.

7 ответов


вам придется реализовать решение для опроса, где вы продолжаете периодически запрашивать содержимое каталога. Сравните это с кэшированным списком из предыдущего вызова и определите, что произошло таким образом.

в протоколе FTP нет ничего, что поможет вам в этом, к сожалению.


на FileSystemWatcher класс работает путем регистрации событий в операционной системе Windows хоста. Таким образом, он ограничен работой над локальными путями и UNC-путями к каталогам, размещенным в системах Windows. Документация MSDN по FileSystemWatcher объясняет путей, которые вы можете использовать и некоторые потенциальные проблемы с помощью класса.

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


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

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

это на самом деле довольно легко реализовать, если вы используете клиентскую библиотеку FTP, которая поддерживает рекурсивный список удаленного дерева. К сожалению, встроенный FTP-клиент .NET,FtpWebRequest нет. Но например с WinSCP .NET assembly версия 5.9 (или новее), вы можете использовать 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 в петля.

session.SynchronizeDirectories(
    SynchronizationMode.Local, "/remote/path", @"C:\local\path", true).Check();

если вы не хотите использовать стороннюю библиотеку, вы должны иметь дело с ограничениями FtpWebRequest. Например, как рекурсивно перечислить удаленное дерево каталогов с помощью FtpWebRequest см. Мой ответ C# Загрузите все файлы и подкаталоги через FTP.


напишите простой сервис для создания FileSystemWatcher, указывая на ваше местоположение ftp.

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

Хав взгляд на:этот блог


вы можете отслеживать местоположение FTP следующим способом:

public class FtpFileSystemWatcher
{

    public bool IsRunning
    {
        get;
        private set;
    }
    public string FtpUserName
    {
        get;
        set;
    }
    public string FtpPassword
    {
        get;
        set;
    }
    public string FtpLocationToWatch
    {
        get;
        set;
    }
    public string DownloadTo
    {
        get;
        set;
    }
    public bool KeepOrignal
    {
        get;
        set;
    }
    public bool OverwriteExisting
    {
        get;
        set;
    }
    public int RecheckIntervalInSeconds
    {
        get;
        set;
    }
    private bool DownloadInprogress
    {
        get;
        set;
    }

    private System.Timers.Timer JobProcessor;

    public FtpFileSystemWatcher(string FtpLocationToWatch = "", string DownloadTo = "", int RecheckIntervalInSeconds = 1, string UserName = "", string Password = "", bool KeepOrignal = false, bool OverwriteExisting = false)
    {
        this.FtpUserName = UserName;
        this.FtpPassword = Password;
        this.FtpLocationToWatch = FtpLocationToWatch;
        this.DownloadTo = DownloadTo;
        this.KeepOrignal = KeepOrignal;
        this.RecheckIntervalInSeconds = RecheckIntervalInSeconds;
        this.OverwriteExisting = OverwriteExisting;

        if (this.RecheckIntervalInSeconds < 1) this.RecheckIntervalInSeconds = 1;
    }

    public void StartDownloading()
    {

        JobProcessor = new Timer(this.RecheckIntervalInSeconds * 1000);
        JobProcessor.AutoReset = false;
        JobProcessor.Enabled = false;
        JobProcessor.Elapsed += (sender, e) =>
        {
            try
            {

                this.IsRunning = true;

                string[] FilesList = GetFilesList(this.FtpLocationToWatch, this.FtpUserName, this.FtpPassword);

                if (FilesList == null || FilesList.Length < 1)
                {
                    return;
                }

                foreach (string FileName in FilesList)
                {
                    if (!string.IsNullOrWhiteSpace(FileName))
                    {
                        DownloadFile(this.FtpLocationToWatch, this.DownloadTo, FileName.Trim(), this.FtpUserName, this.FtpPassword, this.OverwriteExisting);

                        if (!this.KeepOrignal)
                        {
                            DeleteFile(Path.Combine(this.FtpLocationToWatch, FileName.Trim()), this.FtpUserName, this.FtpPassword);
                        }
                    }
                }

                this.IsRunning = false;
                JobProcessor.Enabled = true;                    
            }

            catch (Exception exp)
            {
                this.IsRunning = false;
                JobProcessor.Enabled = true;
                Console.WriteLine(exp.Message);
            }
        };

        JobProcessor.Start();
    }

    public void StopDownloading()
    {
        try
        {
            this.JobProcessor.Dispose();
            this.IsRunning = false;
        }
        catch { }
    }

    private void DeleteFile(string FtpFilePath, string UserName, string Password)
    {
        FtpWebRequest FtpRequest;
        FtpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFilePath));
        FtpRequest.UseBinary = true;
        FtpRequest.Method = WebRequestMethods.Ftp.DeleteFile;

        FtpRequest.Credentials = new NetworkCredential(UserName, Password);
        FtpWebResponse response = (FtpWebResponse)FtpRequest.GetResponse();
        response.Close();

    }
    private void DownloadFile(string FtpLocation, string FileSystemLocation, string FileName, string UserName, string Password, bool OverwriteExisting)
    {
        try
        {
            const int BufferSize = 2048;
            byte[] Buffer = new byte[BufferSize];

            FtpWebRequest Request;
            FtpWebResponse Response;

            if (File.Exists(Path.Combine(FileSystemLocation, FileName)))
            {
                if (OverwriteExisting)
                {
                    File.Delete(Path.Combine(FileSystemLocation, FileName));
                }
                else
                {
                    Console.WriteLine(string.Format("File {0} already exist.", FileName));
                    return;
                }
            }

            Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(Path.Combine(FtpLocation, FileName)));
            Request.Credentials = new NetworkCredential(UserName, Password);
            Request.Proxy = null;
            Request.Method = WebRequestMethods.Ftp.DownloadFile;
            Request.UseBinary = true;

            Response = (FtpWebResponse)Request.GetResponse();

            using (Stream s = Response.GetResponseStream())
            {
                using (FileStream fs = new FileStream(Path.Combine(FileSystemLocation, FileName), FileMode.CreateNew, FileAccess.ReadWrite))
                {
                    while (s.Read(Buffer, 0, BufferSize) != -1)
                    {
                        fs.Write(Buffer, 0, BufferSize);
                    }
                }
            }
        }
        catch { }

    }
    private string[] GetFilesList(string FtpFolderPath, string UserName, string Password)
    {
        try
        {
            FtpWebRequest Request;
            FtpWebResponse Response;

            Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFolderPath));
            Request.Credentials = new NetworkCredential(UserName, Password);
            Request.Proxy = null;
            Request.Method = WebRequestMethods.Ftp.ListDirectory;
            Request.UseBinary = true;

            Response = (FtpWebResponse)Request.GetResponse();
            StreamReader reader = new StreamReader(Response.GetResponseStream());
            string Data = reader.ReadToEnd();

            return Data.Split('\n');
        }
        catch
        {
            return null;
        }
    }


}

способ обработки этого-загрузить массив байтов одного элемента с именем ".ftpComplete". FileSystemWatcher только наблюдает за ".ftpcomplete " файлы и полосы, которые с конца, чтобы узнать фактический загруженный файл. Начиная с".файл "ftpComplete" всего 1 байт, он загружается примерно так же быстро, как он создается на FTP-сервере, поэтому его можно удалить, как только вы сделаете все, что вам нужно, с основным загруженным файлом

        FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(
            FTPAddress + "/" + Path.GetFileName(filePath) + ".ftpComplete");
        request.Method = WebRequestMethods.Ftp.UploadFile;
        request.Credentials = new NetworkCredential(username, password);
        request.UsePassive = true;
        request.UseBinary = true;
        request.KeepAlive = false;
        byte[] buffer = new byte[1];
        Stream reqStream = request.GetRequestStream();
        reqStream.Write(buffer, 0, buffer.Length);
        reqStream.Close();

вы можете использовать скрипт Robo-FTP для отслеживания изменений на FTP-сайте. Вот ссылка на пример скрипта, который отправляет электронное письмо при обнаружении изменения:http://kb.robo-ftp.com/script_library/show/40

Я посмотрел на предыдущий вопрос, который вы связаны. Я думаю, вы должны иметь возможность изменить образец Robo-FTP и использовать SETLEFT команда с параметром / split, чтобы заставить его проанализировать имя папки и номер ISO-файла измененного файла и затем переместите файл в нужное место.