7

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

6 回答 6

10

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

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



    }
}
于 2009-08-06T17:52:59.410 回答
3

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

于 2009-08-06T15:19:32.057 回答
1

这是一个保持同步的实现

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);
                    }
                }
            }
       }
    }
}
于 2009-08-06T18:01:39.937 回答
1

当使用 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;        
    }
    
于 2015-03-21T09:26:42.120 回答
0

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

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

于 2009-08-06T20:42:22.963 回答
0

4年后……

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

于 2014-04-24T19:53:28.117 回答