簡體   English   中英

在沒有FileSystemWatcher的情況下監視目錄以創建新文件

[英]Monitoring a directory for new file creation without FileSystemWatcher

我必須創建一個Windows服務,該服務監視指定文件夾中的新文件並對其進行處理並將其移至其他位置。

我從使用FileSystemWatcher開始。 我的老板不喜歡FileSystemWatcher並且希望我通過使用TimerFileSystemWatcher以外的任何其他機制來使用輪詢。

如何在不使用.NET Framework的FileSystemWatcher情況下監視目錄?

實際上,根據我多年的經驗,FileWatcher組件並不是100%“穩定”的。 將足夠多的文件推送到一個文件夾中,您將丟失一些事件。 如果您監視文件共享,即使您增加了緩沖區大小,尤其如此。

因此,出於所有實際原因,最好將FileWatcher與Timer配合使用,Timer掃描文件夾中的更改,以獲取最佳解決方案。

如果您搜索Google,創建計時器代碼的示例應該很多。 如果您跟蹤計時器運行時的最后一個DateTime,請檢查每個文件的修改日期,並將其與該日期進行比較。 邏輯很簡單。

計時器間隔取決於系統更改的緊急程度。 但是在許多情況下,每分鍾檢查一次應該可以。

使用@Petoj的答案,我包括了一個完整的Windows服務,該服務每五分鍾輪詢一次新文件。 它的局限性在於只有一個線程會輪詢,占用處理時間並支持暫停和及時停止。 它還支持在system.start上輕松附加調試器。

 public partial class Service : ServiceBase{


    List<string> fileList = new List<string>();

    System.Timers.Timer timer;


    public Service()
    {
        timer = new System.Timers.Timer();
        //When autoreset is True there are reentrancy problems.
        timer.AutoReset = false;

        timer.Elapsed += new System.Timers.ElapsedEventHandler(DoStuff);
    }


    private void DoStuff(object sender, System.Timers.ElapsedEventArgs e)
    {
       LastChecked = DateTime.Now;

       string[] files = System.IO.Directory.GetFiles("c:\\", "*", System.IO.SearchOption.AllDirectories);

       foreach (string file in files)
       {
           if (!fileList.Contains(file))
           {
               fileList.Add(file);

               do_some_processing();
           }
       }


       TimeSpan ts = DateTime.Now.Subtract(LastChecked);
       TimeSpan MaxWaitTime = TimeSpan.FromMinutes(5);

       if (MaxWaitTime.Subtract(ts).CompareTo(TimeSpan.Zero) > -1)
           timer.Interval = MaxWaitTime.Subtract(ts).TotalMilliseconds;
       else
           timer.Interval = 1;

       timer.Start();
    }

    protected override void OnPause()
    {
        base.OnPause();
        this.timer.Stop();
    }

    protected override void OnContinue()
    {
        base.OnContinue();
        this.timer.Interval = 1;
        this.timer.Start();
    }

    protected override void OnStop()
    {
        base.OnStop();
        this.timer.Stop();
    }

    protected override void OnStart(string[] args)
    {
       foreach (string arg in args)
       {
           if (arg == "DEBUG_SERVICE")
                   DebugMode();

       }

        #if DEBUG
            DebugMode();
        #endif

        timer.Interval = 1;
        timer.Start();
   }

   private static void DebugMode()
   {
       Debugger.Break();
   }

 }

您可以使用Directory.GetFiles()

using System.IO;

var fileList = new List<string>();

foreach (var file in Directory.GetFiles(@"c:\", "*", SearchOption.AllDirectories))
{
    if (!fileList.Contains(file))
    {
        fileList.Add(file);
        //do something
    }
}

請注意,如果您需要使用FileInfo,則僅檢查新文件而不更改文件

在程序啟動時,使用Directory.GetFiles(path)獲取文件列表。

然后創建一個計時器,並在其過去的事件中調用hasNewFiles:

    static List<string> hasNewFiles(string path, List<string> lastKnownFiles)
    {
        List<string> files = Directory.GetFiles(path).ToList();
        List<string> newFiles = new List<string>();

        foreach (string s in files)
        {
            if (!lastKnownFiles.Contains(s))
                newFiles.Add(s);
        }

        return new List<string>();
    }

在調用代碼中,如果滿足以下條件,您將擁有新文件:

    List<string> newFiles = hasNewFiles(path, lastKnownFiles);
    if (newFiles.Count > 0)
    {
        processFiles(newFiles);
        lastKnownFiles = newFiles;
    }

編輯:如果您想要一個更linqy解決方案:

    static IEnumerable<string> hasNewFiles(string path, List<string> lastKnownFiles)
    {
        return from f in Directory.GetFiles(path) 
               where !lastKnownFiles.Contains(f) 
               select f;
    }

    List<string> newFiles = hasNewFiles(path, lastKnownFiles); 
    if (newFiles.Count() > 0) 
    { 
        processFiles(newFiles); 
        lastKnownFiles = newFiles; 
    } 

我會問為什么不使用FileSystemWatcher。 它在操作系統中注冊,並在文件系統中的事件結束時立即得到通知。

如果確實需要輪詢,則只需創建一個System.Timers.Timer,創建一個要調用的方法,然后在此方法中檢查文件。

是的,您可以創建一個Timer,然后將處理程序插入Elapsed事件中,該事件將為正在查看的目錄實例化DirectoryInfo類,並調用GetFiles()或EnumerateFiles()。 GetFiles()返回一個FileInfo []數組,而EnumerateFiles()返回一個“流” IEnumerable。 如果您希望在查看時該文件夾中有很多文件,則EnumerateFiles()會更高效。 您可以在方法檢索所有FileInfo之前開始使用IEnumerable,而GetFiles將使您等待。

至於為什么它實際上比FileWatcher更好,則取決於幕后的架構。 以基本的提取/轉換/驗證/加載工作流程為例。 首先,這樣的工作流程可能必須創建昂貴的對象實例(數據庫連接,規則引擎的實例等)。 如果工作流的結構可以一次性處理所有可用的事務,那么一次性的開銷將大大減輕。 其次,FileWatcher要求事件處理程序(如此工作流)調用的任何內容都是線程安全的,因為如果文件不斷流入,則許多事件可以立即運行。如果不可行,則可以很容易地配置Timer通過讓事件處理程序檢查線程安全的“進程正在運行”標志來將系統限制為一個正在運行的工作流,並在另一個處理程序線程對其進行了設置但尚未完成時簡單地終止。 與FileWatcher不同,該時間文件夾中的文件將在下次下次啟動計時器時被提取,在FileWatcher中,如果終止處理程序,則有關該文件存在的信息將丟失。

1)聽起來你的老板是個白痴
2)您將必須使用Directory.GetFiles,File.GetLastAccessTime等功能,並將其保留在內存中以檢查其是否已更改。

不能使用FileSystemWatcher或任何可以執行相同功能的Win32 API有點奇怪,但這在當時是無關緊要的。 輪詢方法可能如下所示。

public class WorseFileSystemWatcher : IDisposable
{
  private ManaulResetEvent m_Stop = new ManaulResetEvent(false);

  public event EventHandler Change;

  public WorseFileSystemWatcher(TimeSpan pollingInterval)
  {
    var thread = new Thread(
      () =>
      {
        while (!m_Stop.WaitOne(pollingInterval))
        {
          // Add your code to check for changes here.
          if (/* change detected */)
          {
            if (Change != null)
            {
              Change(this, new EventArgs())
            }
          }
        }
      });
    thread.Start();
  }

  public void Dispose()
  {
    m_Stop.Set();
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM