C#FileSystemWatcher和FTP

我通过文件系统观察器监视丢弃在ftp上的文件,然后移动到另一个目录。 现在我触发了文件系统观察者的创建事件的副本,但显然在ftp的情况下,create只是一个存根文件,数据进入并填充文件,因为它上传到完成。 任何人都有一个优雅的解决方案,或者我必须做我认为我必须做的事情

1 wait till last access time is about n ms in past before I copy 2 throw a control file in there to state that that file is done being copied, then delete control file 3 pound the crap out of it 

这是一个非常天真的实现,但它适合我的目的,我已经看到足够的人在网络上有这个问题所以决定贡献。 考虑到我的问题的性质,实现对我的需求非常具体,我几乎完全不关心改变事件,但是如果他们需要做一些不同的事情,那么人们可以在那里抛出自己的代码,它实际上是创建的,导致最多的问题。 我没有对此进行全面测试,但最初写它看起来不错

 using System; using System.Collections.Generic; using System.IO; using System.Timers; namespace FolderSyncing { public class FTPFileSystemWatcher { private readonly string _path; public event FileSystemEventHandler FTPFileCreated; public event FileSystemEventHandler FTPFileDeleted; public event FileSystemEventHandler FTPFileChanged; private Dictionary _createdFilesToCheck; private readonly object _lockObject = new object(); private const int _milliSecondsSinceLastWrite = 5000; private const int _createdCheckTimerInterval = 2000; private readonly FileSystemWatcher _baseWatcher; public FTPFileSystemWatcher(string path, string Filter) { _path = path; _baseWatcher = new FileSystemWatcher(path,Filter); SetUpEventHandling(); } public FTPFileSystemWatcher(string path) { _path = path; _baseWatcher = new FileSystemWatcher(path); SetUpEventHandling(); } private void SetUpEventHandling() { _createdFilesToCheck = new Dictionary(); Timer copyTimer = new Timer(_createdCheckTimerInterval); copyTimer.Elapsed += copyTimer_Elapsed; copyTimer.Enabled = true; copyTimer.Start(); _baseWatcher.EnableRaisingEvents = true; _baseWatcher.Created += _baseWatcher_Created; _baseWatcher.Deleted += _baseWatcher_Deleted; _baseWatcher.Changed += _baseWatcher_Changed; } void copyTimer_Elapsed(object sender, ElapsedEventArgs e) { lock (_lockObject) { Console.WriteLine("Checking : " + DateTime.Now); DateTime dateToCheck = DateTime.Now; List toRemove = new List(); foreach (KeyValuePair fileToCopy in _createdFilesToCheck) { FileInfo fileToCheck = new FileInfo(_path + fileToCopy.Key); TimeSpan difference = fileToCheck.LastWriteTime - fileToCopy.Value.Date; fileToCopy.Value.Update(fileToCopy.Value.Date.AddMilliseconds(difference.TotalMilliseconds)); if (fileToCopy.Value.Date.AddMilliseconds(_milliSecondsSinceLastWrite) < dateToCheck) { FileSystemEventArgs args = new FileSystemEventArgs(WatcherChangeTypes.Created, _path, fileToCopy.Key); toRemove.Add(fileToCopy.Key); InvokeFTPFileCreated(args); } } foreach (string removal in toRemove) { _createdFilesToCheck.Remove(removal); } } } void _baseWatcher_Changed(object sender, FileSystemEventArgs e) { InvokeFTPFileChanged(e); } void _baseWatcher_Deleted(object sender, FileSystemEventArgs e) { InvokeFTPFileDeleted(e); } void _baseWatcher_Created(object sender, FileSystemEventArgs e) { if (!_createdFilesToCheck.ContainsKey(e.Name)) { FileInfo fileToCopy = new FileInfo(e.FullPath); _createdFilesToCheck.Add(e.Name,new LastWriteTime(fileToCopy.LastWriteTime)); } } private void InvokeFTPFileChanged(FileSystemEventArgs e) { FileSystemEventHandler Handler = FTPFileChanged; if (Handler != null) { Handler(this, e); } } private void InvokeFTPFileDeleted(FileSystemEventArgs e) { FileSystemEventHandler Handler = FTPFileDeleted; if (Handler != null) { Handler(this, e); } } private void InvokeFTPFileCreated(FileSystemEventArgs e) { FileSystemEventHandler Handler = FTPFileCreated; if (Handler != null) { Handler(this, e); } } } public class LastWriteTime { private DateTime _date; public DateTime Date { get { return _date; } } public LastWriteTime(DateTime date) { _date = date; } public void Update(DateTime dateTime) { _date = dateTime; } } } 

等到你可以专门打开文件 – 我不会说这是一个很好的解决方案,但在这种情况下可能是最安全的策略。

这是一个保持同步的实现

 using System; using System.Configuration; using System.IO; using System.Threading; namespace FolderSyncing { public class FolderSync { private readonly string masterDirectoryPath; private readonly string slaveDirectoryPath; public FolderSync() { masterDirectoryPath = ConfigurationManager.AppSettings.Get("MasterDirectory"); slaveDirectoryPath = ConfigurationManager.AppSettings.Get("SlaveDirectory"); if (Directory.Exists(masterDirectoryPath) && Directory.Exists(slaveDirectoryPath)) { FTPFileSystemWatcher watcher = new FTPFileSystemWatcher(masterDirectoryPath); watcher.FTPFileChanged += watcher_FTPFileChanged; watcher.FTPFileCreated += watcher_FTPFileCreated; watcher.FTPFileDeleted += watcher_FTPFileDeleted; } else { Console.WriteLine("Directories were not located check config paths"); } } void watcher_FTPFileDeleted(object sender, FileSystemEventArgs e) { DeleteFile(slaveDirectoryPath + e.Name, 5, 1); } void watcher_FTPFileCreated(object sender, FileSystemEventArgs e) { CopyFile(e.Name,5,1); } void watcher_FTPFileChanged(object sender, FileSystemEventArgs e) { } private void DeleteFile(string fullPath, int attempts, int attemptNo) { if (File.Exists(fullPath)) { try { File.Delete(fullPath); Console.WriteLine("Deleted " + fullPath); } catch (Exception) { if (attempts > attemptNo) { Console.WriteLine("Failed deleting " + fullPath + "trying again "+ attemptNo.ToString()+ " of "+attempts); Thread.Sleep(500); DeleteFile(fullPath, attempts, attemptNo + 1); } else { Console.WriteLine("Critically Failed deleting " + fullPath); } } } } private void CopyFile(string fileName,int attempts, int attemptNo) { string masterFile = masterDirectoryPath + fileName; string slaveFile = slaveDirectoryPath + fileName; if (File.Exists(masterFile)) { try { File.Copy(masterFile,slaveFile); Console.WriteLine("Copied " + masterFile); } catch (Exception) { if (attempts > attemptNo) { Console.WriteLine("Failed copying " + masterFile + "trying again " + attemptNo.ToString() + " of " + attempts); Thread.Sleep(500); CopyFile(fileName, attempts, attemptNo + 1); } else { Console.WriteLine("Critically Failed copying " + masterFile); } } } } } } 

当使用FTP从另一台服务器复制文件时,在完成文件复制之前,文件名将被重命名为额外的扩展名,如.TMP,如下面的路径示例所示。

C:\ InterfaceServer \ OilAndGas \ XMLForTest \ TestStbFile.xml.TMP

要克服这种情况,请遵循两个步骤

  1. 当存在文件观察器方法的运行时,FileSystemEventArgs参数包含文件名,其附加文件名的附加文件扩展名,因为它刚到达文件夹而未完成复制操作。
  2. 您只需要调用以下方法来删除额外的扩展,并在代码中添加等待2秒,以便创建完整的文件,您可以使用它进行进一步处理。

     public static string VerifyIfStubFile(string filePath, string extension) { if (filePath.Length - (filePath.IndexOf(extension) + extension.Length) != 0) { /*LogMsg("Info : VerifyIfStubFile : Stub file found. Lets fix it!!"); */ return filePath = filePath.Substring(0, (filePath.IndexOf(extension) + extension.Length)); } return filePath; } 

让源在数据文件完成后直接上传存根文件,并让FileSystemWatcher监视存根文件。 例如,如果数据文件名是mydata_01234,则存根woulb为mydata_01234_stub。 然后FileSystemWatcher应该有一个“* _stub”的掩码。 然后通过剥离“_stub”后缀来了解数据文件名。 并且在数据文件完成之后才能上传存根文件,因此数据文件将是空闲的。

如果存根文件只是一个字节,那么您应该能够在使用数据文件执行的任何操作之后删除它们而不会出现问题。 如果您的操作特别快,请在删除存根之前添加100毫秒的hibernate时间。

4年后….

存根文件是一个好主意,但是,可能稍微更健壮的方法是让你的源首先创建存根文件,上传你的“真实”文件,然后删除存根。