如何实现FileSystemWatcher
FTP 位置(在 C# 中)。这个想法是每当在 FTP 位置添加任何内容时,我都希望将其复制到我的本地计算机上。任何想法都会有所帮助。
7 回答
您将不得不实施一个轮询解决方案,在该解决方案中您不断地定期询问目录内容。将此与上一次调用中的缓存列表进行比较,并确定以这种方式发生了什么。
不幸的是,FTP 协议中没有任何东西可以帮助您解决这个问题。
您不能使用FileSystemWatcher
或任何其他方式,因为 FTP 协议没有任何 API 来通知客户端远程目录中的更改。
您所能做的就是定期迭代远程树并查找更改。
如果您使用支持远程树递归列表的 FTP 客户端库,它实际上很容易实现。不幸的是,内置的 .NET FTP 客户端FtpWebRequest
没有。但例如对于WinSCP .NET 程序集,您可以使用该Session.EnumerateRemoteFiles
方法。
请参阅文章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();
请参阅文章 保持本地目录最新(从远程 SFTP/FTP 服务器下载更改的文件)。
如果您不想使用 3rd 方库,则必须处理FtpWebRequest
. 有关如何使用 递归列出远程目录树的示例,请参阅我对C# Download all files and subdirectories through FTPFtpWebRequest
的回答。
该类FileSystemWatcher
通过向主机 Windows 操作系统注册事件来工作。因此,它仅限于处理 Windows 系统上托管的目录的本地路径和 UNC 路径。MSDN 文档FileSystemWatcher
解释了您可以使用的路径以及使用该类的一些潜在问题。
如果您希望收到有关 FTP 站点更改的警报,则必须使用轮询机制来询问您有兴趣监视的文件或文件夹的当前状态。您将能够通过比较 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 示例并使用带有 /split 选项的SETLEFT命令使其解析更改文件的文件夹名称和 ISO 文件编号,然后将文件移动到正确的位置。