我必须创建一个Windows服务,它监视指定文件夹中的新文件并对其进行处理并将其移动到其他位置。
我开始使用FileSystemWatcher
。我的老板不喜欢FileSystemWatcher
,并希望我使用Timer
或除FileSystemWatcher
以外的任何其他机制来使用轮询。
如何在不使用.NET框架的FileSystemWatcher
的情况下监控目录?
答案 0 :(得分:17)
实际上,根据我多年来的经验,FileWatcher组件并非100%“稳定”。将足够多的文件压入文件夹,您将丢失一些事件。如果监视文件共享,即使增加缓冲区大小,也是如此。
因此,出于所有实际原因,使用FileWatcher和Timer扫描文件夹中的更改,以获得最佳解决方案。
如果你谷歌它,创建计时器代码的例子应该是丰富的。如果在计时器运行时跟踪最后一个DateTime,则检查每个文件的修改日期,并将其与日期进行比较。相当简单的逻辑。
计时器间隔取决于系统更改的紧急程度。但是对于很多场景来说,检查每一分钟都应该没问题。
答案 1 :(得分:6)
使用@ Petoj的答案我已经包含了一个完整的Windows服务,每五分钟轮询一次新文件。它只限于一个线程轮询,占用处理时间并支持暂停和及时停止。它还支持在system.start上轻松附加debbugger
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();
}
}
答案 2 :(得分:4)
您可以使用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
,则仅检查新文件未更改的文件答案 3 :(得分:4)
在程序启动时,使用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;
}
编辑:如果你想要一个更有效的解决方案:
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;
}
答案 4 :(得分:1)
我会问为什么不使用FileSystemWatcher。它向操作系统注册,并在事件在文件系统中完成时立即得到通知。
如果你真的需要轮询,那么只需创建一个System.Timers.Timer,创建一个方法让它调用,并检查这个方法中的文件。
答案 5 :(得分:1)
是的,您可以创建一个Timer,并将处理程序插入到Elapsed事件中,该事件将为您正在观看的目录实例化DirectoryInfo类,并调用GetFiles()或EnumerateFiles()。 GetFiles()返回一个FileInfo []数组,而EnumerateFiles()返回一个“流”IEnumerable。如果你期望看到很多文件在该文件夹中,EnumerateFiles()会更有效率;你可以在方法检索到所有FileInfos之前开始使用IEnumerable,而GetFiles会让你等待。
至于为什么这可能实际上比FileWatcher更好,它取决于幕后的架构。例如,采用基本的提取/转换/验证/加载工作流程。首先,这样的工作流可能必须创建昂贵的对象实例(DB连接,规则引擎的实例等)。如果工作流程的结构能够一次性处理可用的所有内容,则可以显着减轻这种一次性开销。其次,FileWatcher需要事件处理程序调用的任何东西,比如这个工作流,是线程安全的,因为如果文件不断流入,MANY事件可以立即运行。如果这不可行,可以非常容易地配置Timer通过让事件处理程序检查线程安全的“进程运行”标志并在另一个处理程序线程已设置但尚未完成的情况下终止,将系统限制为一个正在运行的工作流。下次定时器触发时,文件夹中的文件将被拾取,这与FileWatcher不同,如果你终止处理程序,那么有关该文件存在的信息就会丢失。
答案 6 :(得分:0)
1)听起来你的老板是个白痴 2)您必须使用Directory.GetFiles,File.GetLastAccessTime等函数并将其保存在内存中以检查它是否发生了变化。
答案 7 :(得分:0)
有点奇怪的是,你不能使用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();
}
}