我监控通过文件系统观察家对FTP丢弃的文件,然后移动到另一个目录。 现在我触发副本掉的文件系统观察者的创建事件,但显然在FTP中创建的情况下,仅仅是一个存根文件和数据进来,填补了文件,因为它上传至完成。 任何人都有这个优雅的解决方案,或做我必须做什么,我想我必须做
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<string, LastWriteTime> _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<string, LastWriteTime>();
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<string> toRemove = new List<string>();
foreach (KeyValuePair<string, LastWriteTime> 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
为了克服这种情况,遵循两个步骤
- 当运行文件观察者方法,FileSystemEventArgs参数包含附加额外的文件扩展名,因为它刚来的文件夹中,而不是与复制操作完成的文件名文件名。
您需要只需要调用下面的方法来去除多余的扩展,从而使完整的文件被创建在代码中添加等待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毫秒的睡眠。
4年之后....
存根文件是一个好主意,但是,也许这样做是让你的源首先创建一个存根文件,上传你的“真实”的文件,然后删除存根一个稍微更稳健的方式。