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

これは非常に単純な実装ですが、私の目的には合っています。Web 上でこの問題を抱えている人を十分に見かけたので、貢献することにしました。実装は私のニーズにかなり固有のものであり、私の問題の性質を考えると、変更されたイベントにはほとんど関心がありませんが、何か違うことをする必要がある場合は、そこに独自のコードを投げることができます。私はこれを完全にテストしていませんが、最初の書き込みでは良さそうです

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 に答える
0

データファイルが完了した直後にソースにスタブファイルをアップロードさせ、FileSystemWatcherにスタブファイルを監視させます。たとえば、データファイル名がmydata_01234の場合、スタブはmydata_01234_stubになります。その場合、FileSystemWatcherには「*_stub」のマスクが必要です。次に、「_ stub」サフィックスを削除することで、データファイル名がわかります。また、スタブファイルはデータファイルが完了するまでアップロードできないため、データファイルは解放されます。

スタブファイルがそれぞれ1バイトしかない場合は、データファイルで実行している操作の後で、問題なくスタブファイルを削除できるはずです。操作が特に高速な場合は、スタブを削除する前に100ミリ秒のスリープを追加してください。

于 2009-08-06T20:42:22.963 に答える
0

それから4年……。

スタブ ファイルを使用することは良い考えですが、ソースで最初にスタブ ファイルを作成し、「実際の」ファイルをアップロードしてから、スタブを削除するのが、おそらくもう少し確実な方法です。

于 2014-04-24T19:53:28.117 に答える