C# FileSystemWatcher 和 FTP

发布于 2024-08-01 23:58:52 字数 367 浏览 6 评论 0原文

我通过文件系统观察器监视在 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

I monitor files that are dropped on a ftp via filesystem watcher then move to another dir. Now I trigger the copy off the create event of the filesystem watcher but obviously in the case of ftp the create is just a stub file and the data comes in and fills the file as it uploads till complete. Anyone have an elegant solution for this, or do I have to do what I think I have to do

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

红颜悴 2024-08-08 23:58:52

这是一个非常幼稚的实现,但它适合我的目的,我在网上看到了足够多的人遇到这个问题,所以决定做出贡献。 该实现是针对我的需求的,鉴于我的问题的性质,我几乎完全不关心更改的事件,但如果人们需要做一些不同的事情,他们可以将自己的代码放入其中,这确实是导致最多问题的创建。 我还没有完全测试过这个,但乍一看它看起来不错

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;
        }



    }
}

this is a very naive implementation but it suits my purposes, i have seen enough people with this problem on the web so decided to contribute. The implementation is fairly specific to my needs I almost completely unconcerned with changed events given the nature of my problem but people can throw their own code in there if they need to do something different, it really the created which cause the most issues. I havent fully tested this but at first write it looks good

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;
        }



    }
}
蓝眼睛不忧郁 2024-08-08 23:58:52

等到您可以独占方式打开文件 - 我不会说这是一个很好的解决方案,但可能是这种情况下最安全的策略。

Wait until you can exclusively open the file- I wouldn't go as far to say that it is a nice solution, but probably the safest strategy in the circumstances.

挽手叙旧 2024-08-08 23:58:52

这是保持同步的实现

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);
                    }
                }
            }
       }
    }
}

here's an implementation to keep in sync

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);
                    }
                }
            }
       }
    }
}
热鲨 2024-08-08 23:58:52

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

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

要克服这种情况,请执行以下两个步骤

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

    公共静态字符串VerifyIfStubFile(字符串文件路径,字符串扩展名) 
      {              
          if (filePath.Length - (filePath.IndexOf(扩展名) + 扩展名.Length) != 0)             
          {                 
              /*LogMsg("信息:VerifyIfStubFile:找到存根文件。让我们修复它!!");   */                
              return filePath = filePath.Substring(0, (filePath.IndexOf(扩展名) + 扩展名.Length));             
          }             
          返回文件路径;         
      } 
      

When file gets copied from another server using FTP, before complete file copy , file name gets renamed with extra extension like .TMP as shown in path example below.

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

To overcome this situation follow two steps

  1. When there is run of file watcher method, FileSystemEventArgs
    parameter contains file name with appended extra file extension to
    the file name as it just arrived in the folder and not completed
    with copy operation.
  2. You need to just call the below method to remove the extra extension
    and add wait for 2 seconds in the code so that the complete file
    gets created and you can use it for further processing.

    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;        
    }
    
十六岁半 2024-08-08 23:58:52

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

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

Have the source upload a stub file directly after the data file completes, and have your FileSystemWatcher watch for the stub file. For example, if the data file name is mydata_01234 then the stub woulb be mydata_01234_stub. The FileSystemWatcher should then have a mask of "*_stub". You then know the data file name by stripping off the "_stub" suffix. And the stub file cannot be uploaded until after the data file completes, so the data file will be free.

If the stub files are just one byte each you should be able to remove them after whatever operation you're performing with the data file without issue. If your operations are particularly fast, add a 100 ms sleep before deleting the stub.

世界和平 2024-08-08 23:58:52

4 年后......

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

4 years later....

the stub file is a good idea, but, probably a slightly more robust way to do it is to have your source create a stub file first, upload your "real" file, then delete the stub.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文