繁体   English   中英

在反应式扩展中包装文件观察程序

[英]Wrap a file watcher in reactive extensions

我一直在寻找将文件观察器包装在一个可观察的文件中以帮助处理事件,但是我在弄清楚如何从中获取我想要的行为时遇到了一些麻烦。 文件监视器监视放置文件的目录。 首次将文件放入该目录时,将在文件观察器上触发Created事件。 但是,如果文件很大或网络连接速度很慢,则在文件更新时会触发一系列Changed事件。 我不想处理文件,直到它完成编写,所以我真正需要的是这个时间轴

|Created    |Changed   |Changed   |Changed                      
________________________________________________ 
^Write starts       ^Write finishes       ^Processing Starts    

我查看了一些过滤Rx中事件的方法,但是我无法得到我需要的东西,“一旦文件文件没有被改变X秒就触发一个函数”。 节流是不好的,因为它会在中间丢失事件。 缓冲区不好,因为缓冲区边界上可能发生事件。

我曾经考虑过使用超时但是我并没有因为他们抛出异常而感到疯狂,我希望处理在文件写入时开始,而不是一旦没有更多的事件。

Reactive Extensions与FileSystemWatcher有一个类似的问题,但从未真正解决过。

有没有一种方法可以让我轻松完成这项工作? 我确信这不是一个不常见的用例。

编辑:经过审查,不要以为你想要这个......

Mayhap我有点过分简化了,但是 Throttle在这里不是理想的吗?

这绝不是“简单”,但我认为它比你之前的想法做得更接近:

(奖金:有测试用例!;))

void Main()
{
    var pathToWatch = @"c:\temp\";
    var fsw = new FileSystemWatcher(pathToWatch);

    // set up observables for create and changed
    var changedObs = 
       Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
          dlgt => fsw.Changed += dlgt, 
          dlgt => fsw.Changed -= dlgt);
    var createdObs = 
       Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>( 
          dlgt => fsw.Created += dlgt, 
          dlgt => fsw.Created -= dlgt);

    // the longest we'll wait between last file write and calling it "changed"
    var maximumTimeBetweenWrites = TimeSpan.FromSeconds(1);

    // A "pulse" ticking off every 10ms (adjust this as desired)
    var timer = Observable
        .Timer(TimeSpan.Zero, TimeSpan.FromMilliseconds(10))
        .Select(i => DateTime.Now);

    var watcher = 
        from creation in createdObs
        from change in changedObs
            // we only care about changes matching a create
            .Where(changeEvt => changeEvt.EventArgs.Name == creation.EventArgs.Name)
            // take latest of (pulse, changes) and select (event, time since last file write)
            .CombineLatest(timer, (evt, now) => new {
                    Change = evt, 
                    DeltaFromLast = now.Subtract(new FileInfo(evt.EventArgs.FullPath).LastWriteTime)})
            // skip all until we trigger than "time before considered changed" threshold
            .SkipWhile(evt => evt.DeltaFromLast < maximumTimeBetweenWrites)
            // Then lock on that until we change a diff file
            .Distinct(evt => evt.Change.EventArgs.FullPath)
        select change.Change;

    var disp = new CompositeDisposable();

    // to show creates
    disp.Add(
        createdObs.Subscribe(
           evt => Console.WriteLine("New file:{0}", 
                evt.EventArgs.FullPath)));

    // to show "final changes"
    disp.Add(
        watcher.Subscribe(
           evt => Console.WriteLine("{0}:{1}:{2}", 
                 evt.EventArgs.Name, 
                 evt.EventArgs.ChangeType, 
                 evt.EventArgs.FullPath)));

    fsw.EnableRaisingEvents = true;

    var rnd = new Random();
    Enumerable.Range(0,10)
        .AsParallel()
        .ForAll(i => 
            {
                var filename = Path.Combine(pathToWatch, "foo" + i + ".txt");
                if(File.Exists(filename))
                    File.Delete(filename);

                foreach(var j in Enumerable.Range(0, 20))
                {
                    var writer = File.AppendText(filename);
                    writer.WriteLine(j);
                    writer.Close();
                    Thread.Sleep(rnd.Next(500));
                }
            });

    Console.WriteLine("Press enter to quit...");
    Console.ReadLine();
    disp.Dispose();        
}

ObservableFileSystemWatcher - FileSystemWatcher类型的可观察包装器 - 完美地工作。 添加名为ReactiveFileSystemWatcher的NuGet包并创建一个控制台应用程序以进行如下测试

class Program
{
  static void Main(string[] args)
  {
    using (var watcher = new ObservableFileSystemWatcher(c => { c.Path = @"C:\FolderToWatch\"; c.IncludeSubdirectories = true; }))
    {
      watcher.Created.Select(x => $"{x.Name} was {x.ChangeType}").Subscribe(Console.WriteLine);
      watcher.Changed.Select(x => $"{x.Name} was {x.ChangeType}").Subscribe(Console.WriteLine);
      watcher.Renamed.Select(x => $"{x.OldName} was {x.ChangeType} to {x.Name}").Subscribe(Console.WriteLine);
      watcher.Deleted.Select(x => $"{x.Name} was {x.ChangeType}").Subscribe(Console.WriteLine);
      watcher.Errors.Subscribe(Console.WriteLine);
      watcher.Start();
      Console.ReadLine();
    }
  }
}

这个答案 BufferWithInactivity一下我的BufferWithInactivity扩展方法。

我认为您可以使用它来查找已更改事件中的不活动状态。

查看NuGet包Reactive FileSystemWatcher

源代码位于GitHub页面上

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM