FTP的FileSystemWatcher

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

这是我之前的问题的后续跟进使用.NET的选择性FTP下载 。

您将不得不实施一个轮询解决方案,您可以定期询问目录内容。 将此与前一次调用的缓存列表进行比较,并确定发生了什么。

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

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

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

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

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

如果您使用支持远程树的递归列表的FTP客户端库,它实际上很容易实现。 不幸的是,内置的.NET FTP客户端, FtpWebRequest没有。 但是,例如使用WinSCP .NET程序集版本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 prevFiles = null; while (true) { // Collect file list List 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 added = files.Except(prevFiles); if (added.Any()) { Console.WriteLine("Added files:"); foreach (string path in added) { Console.WriteLine(path); } } IEnumerable 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位置。

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

看看这个博客

您可以通过以下方法监视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示例并使用带有/ split选项的SETLEFT命令使其解析更改文件的文件夹名称和ISO文件编号,然后将文件移动到正确的位置。