C# 将文件监视程序包装为被动扩展名

C# 将文件监视程序包装为被动扩展名,c#,system.reactive,C#,System.reactive,我一直在考虑将一个文件观察者包装在一个可观察的容器中,以帮助处理事件,但我在弄清楚如何从中获得我想要的行为方面遇到了一些困难。文件监视程序监视文件所在的目录。当文件第一次放入该目录时,将在文件监视程序上触发创建的事件。但是,如果文件较大或网络连接较慢,则会在文件更新时触发一系列更改事件。我不想处理文件,直到它已经完成编写,所以我真正需要的是这个时间表 |Created |Changed |Changed |Changed ________

我一直在考虑将一个文件观察者包装在一个可观察的容器中,以帮助处理事件,但我在弄清楚如何从中获得我想要的行为方面遇到了一些困难。文件监视程序监视文件所在的目录。当文件第一次放入该目录时,将在文件监视程序上触发创建的事件。但是,如果文件较大或网络连接较慢,则会在文件更新时触发一系列更改事件。我不想处理文件,直到它已经完成编写,所以我真正需要的是这个时间表

|Created    |Changed   |Changed   |Changed                      
________________________________________________ 
^Write starts       ^Write finishes       ^Processing Starts    
我研究了许多过滤Rx中事件的方法,但我无法得到我需要的是“在X秒内未更改文件时启动函数”。节气门不好,因为它会在中间输掉比赛。缓冲区不好,因为事件可能发生在缓冲区边界上

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

有一个类似的问题从未真正解决过


有没有一种方法可以让我轻松做到这一点?我相信这不是一个罕见的用例

编辑:回顾之后,不要认为你想要这个

也许我有点过于简单化了,但是
Throttle
在这里不是很理想吗

这绝非“简单”,但我认为它比我之前的想法更符合你的要求:

(奖励:有一个测试用例!;)

void Main()
{
var pathToWatch=@“c:\temp\”;
var fsw=新的文件系统监视程序(pathToWatch);
//为创建和更改设置可观察对象
var changedObs=
可观察的。从事件模式(
dlgt=>fsw.Changed+=dlgt,
dlgt=>fsw.Changed-=dlgt);
var createdObs=
可观察。FromEventPattern(
dlgt=>fsw.Created+=dlgt,
dlgt=>fsw.Created-=dlgt);
//从最后一次写入文件到将其称为“已更改”之间的最长等待时间
var maximumTimeBetweenWrites=从秒开始的时间跨度(1);
//每10毫秒一次“脉冲”滴答作响(根据需要进行调整)
var定时器=可观测
.Timer(TimeSpan.Zero,TimeSpan.From毫秒(10))
.Select(i=>DateTime.Now);
变量观察程序=
从createdObs中的创建
从changedObs中的change
//我们只关心与创建匹配的更改
.Where(changeEvt=>changeEvt.EventArgs.Name==creation.EventArgs.Name)
//获取最新的(脉冲、更改)并选择(事件、自上次文件写入以来的时间)
.CombineTest(计时器,(evt,现在)=>新建{
变更=evt,
DeltaFromLast=now.Subtract(新文件信息(evt.EventArgs.FullPath.LastWriteTime)})
//跳过所有,直到触发超过“考虑更改之前的时间”阈值
.SkipWhile(evt=>evt.DeltaFromLast<两次写入之间的最大时间)
//然后锁定它,直到我们更改一个diff文件
.Distinct(evt=>evt.Change.EventArgs.FullPath)
选择change.change;
var disp=新的CompositeDisposable();
//展示创造
显示添加(
createdObs.Subscribe(
evt=>Console.WriteLine(“新文件:{0}”),
evt.EventArgs.FullPath));
//显示“最终更改”
显示添加(
观察者,订阅(
evt=>Console.WriteLine(“{0}:{1}:{2}”,
evt.EventArgs.Name,
evt.EventArgs.ChangeType,
evt.EventArgs.FullPath));
fsw.EnableRaisingEvents=true;
var rnd=新随机数();
可枚举范围(0,10)
.天冬酰胺()
.ForAll(i=>
{
var filename=Path.Combine(pathToWatch,“foo”+i+“.txt”);
if(File.Exists(filename))
删除(文件名);
foreach(可枚举范围(0,20)中的var j)
{
var writer=File.AppendText(文件名);
作者:WriteLine(j);
writer.Close();
线程睡眠(rnd.Next(500));
}
});
Console.WriteLine(“按enter键退出…”);
Console.ReadLine();
disp.Dispose();
}

查看中我的
BufferWithInactivity
扩展方法


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

查看NuGet包


源代码位于上。

ObservableFileSystemWatcher
-一个可观察的包装器-可以完美地工作。添加一个名为
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();
    }
  }
}

我没有对我的单元测试抛出这个,但我认为它不会起作用,因为更改的事件并不总是被触发。顺序是,您有一个已创建的事件,后跟0个或多个已更改的事件。我也不相信这能解决文件写入时间超过5秒的问题。@stimms实际上,完全忘了我回答了这个问题…仔细看,我想这不是你想要的…让我更新一下…@stimms ok,一个新的概念-创建应该允许您在文件进入时开始处理文件,“最终更改”应该在文件“完成”时发出信号。非常感谢您在这方面所做的努力。我想我最终得到了一些使用BufferWithInactivity可以工作的东西,但我已经学到了很多,仅仅是阅读你的代码。@stimms不用担心,实际上写起来很有趣(嗯,也许不有趣……很吸引人):)我使用不活动的缓冲区加上一个分组函数来实现我想要的。
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();
    }
  }
}