【问题标题】:FileSystemWatcher for FTP用于 FTP 的 FileSystemWatcher
【发布时间】:2010-11-19 14:52:07
【问题描述】:

如何为 FTP 位置(在 C# 中)实现 FileSystemWatcher。这个想法是每当在 FTP 位置添加任何内容时,我都希望将其复制到我的本地计算机上。任何想法都会有所帮助。

这是我之前的问题Selective FTP download using .NET的跟进。

【问题讨论】:

  • 您将使用轮询类型的方法。您必须定期检查 ftp 站点以检查是否有新文件。

标签: c# .net ftp filesystemwatcher


【解决方案1】:

您将不得不实施一个轮询解决方案,在该解决方案中您不断地定期询问目录内容。将此与上一次调用中的缓存列表进行比较,然后确定发生了什么。

不幸的是,FTP 协议中没有任何东西可以帮助您解决这个问题。

【讨论】:

    【解决方案2】:

    您不能使用FileSystemWatcher 或任何其他方式,因为 FTP 协议没有任何 API 来通知客户端远程目录的更改。

    您所能做的就是定期迭代远程树并查找更改。

    如果您使用支持远程树递归列表的 FTP 客户端库,它实际上很容易实现。不幸的是,内置的 .NET FTP 客户端 FtpWebRequest 没有。但例如使用WinSCP .NET assembly,您可以使用Session.EnumerateRemoteFiles method

    见文章Watching for changes in SFTP/FTP server

    // 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();
    

    见文章Keep local directory up to date (download changed files from remote SFTP/FTP server)


    如果您不想使用第 3 方库,则必须处理 FtpWebRequest 的限制。有关如何使用FtpWebRequest 递归列出远程目录树的示例,请参阅我对C# Download all files and subdirectories through FTP 的回答。

    【讨论】:

      【解决方案3】:

      FileSystemWatcher 类通过向主机 Windows 操作系统注册事件来工作。因此,它仅限于处理 Windows 系统上托管的目录的本地路径和 UNC 路径。 FileSystemWatcher 上的 MSDN 文档解释了您可以使用的路径以及使用该类的一些潜在问题。

      如果您希望收到有关 FTP 站点更改的警报,则必须使用轮询机制来询问您有兴趣监视的文件或文件夹的当前状态。您将能够通过比较 FTP 站点的快照的更改并在检测到更改时引发类似事件来查看何时添加和删除文件。不幸的是,您将无法检测到重命名事件,但其他更改应该很容易通过这种方式进行监控。

      【讨论】:

        【解决方案4】:

        编写一个简单的服务来创建 FileSystemWatcher,指向您的 ftp 位置。

        然后,当上传或修改文件时,将在您的服务中触发一个事件,然后您可以使用该事件将文件复制到本地计算机。
        文件.复制等

        看看:this blog

        【讨论】:

        • 您不能使用 UNC 路径来引用 FTP 上的位置。 UNC 路径暗示 SMB/本地 Windows 路径。
        【解决方案5】:

        您可以通过以下方法监控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;
                }
            }
        
        
        }
        

        【讨论】:

        • 未来读者的命名空间:使用系统;使用 System.IO;使用 System.Net;使用 System.Timers;
        【解决方案6】:

        我处理这个问题的方法是上传一个名为“.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();
        

        【讨论】:

          【解决方案7】:

          您可以使用 Robo-FTP 脚本来监控 FTP 站点的更改。这是一个示例脚本的链接,该脚本在检测到更改时发送电子邮件:http://kb.robo-ftp.com/script_library/show/40

          我查看了您链接的上一个问题。我认为您应该能够修改 Robo-FTP 示例并使用带有 /split 选项的 SETLEFT 命令使其解析更改文件的文件夹名称和 ISO 文件编号,然后将文件移动到正确的位置。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-04-11
            • 1970-01-01
            • 2023-03-13
            • 1970-01-01
            相关资源
            最近更新 更多