.net 在没有 FileSystemWatcher 的情况下监视目录以创建新文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3612311/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Monitoring a directory for new file creation without FileSystemWatcher
提问by Abbi
I have to create a Windows service which monitors a specified folder for new files and processes it and moves it to other location.
我必须创建一个 Windows 服务,它监视指定文件夹中的新文件并处理它并将其移动到其他位置。
I started with using FileSystemWatcher. My boss doesn't like FileSystemWatcherand wants me to use polling by using a Timeror any other mechanism other than FileSystemWatcher.
我开始使用FileSystemWatcher. 我的老板不喜欢FileSystemWatcher并希望我通过使用 aTimer或除FileSystemWatcher.
How can you monitor directorying without using FileSystemWatcherusing .NET framework?
如何在不FileSystemWatcher使用 .NET 框架的情况下监控目录?
采纳答案by Conrad Frix
Using @Petoj's answer I've included a full windows service that polls every five minutes for new files. Its contrained so only one thread polls, accounts for processing time and supports pause and timely stopping. It also supports easy attaching of a debbugger on system.start
使用@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();
}
}
回答by Mikael Svenson
Actually, the FileWatcher component is not 100% "stable" from my experience over the years. Push enough files into a folder and you will lose some events. This is especially true if you monitor a file share, even if you increase the buffer size.
实际上,根据我多年来的经验,FileWatcher 组件并不是 100%“稳定”的。将足够多的文件推送到文件夹中,您将丢失一些事件。如果您监视文件共享,则尤其如此,即使您增加了缓冲区大小。
So, for all practical reasons, use FileWatcher together with a Timer which scans a folder for changes, for the most optimal solution.
因此,出于所有实际原因,将 FileWatcher 与扫描文件夹更改的计时器一起使用,以获得最佳解决方案。
Examples of creating Timer code should be in abundance if you google it. If you keep track of the last DateTime when the timer ran, then check the modified date of each file, and compare it to the date. Fairly simple logic.
如果你用谷歌搜索,创建定时器代码的例子应该很多。如果您在计时器运行时跟踪上次 DateTime,则检查每个文件的修改日期,并将其与日期进行比较。相当简单的逻辑。
The timer interval depends of how urgent the changes are for your system. But check every minute should be fine for many scenarios.
计时器间隔取决于更改对您的系统的紧急程度。但是对于许多情况,每分钟检查一次应该没问题。
回答by Steven Evers
At program startup, use Directory.GetFiles(path) to get the list of files.
在程序启动时,使用 Directory.GetFiles(path) 来获取文件列表。
Then create a timer, and in its elapsed event call hasNewFiles:
然后创建一个计时器,并在其经过的事件中调用 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>();
}
In the calling code, you'll have new files if:
在调用代码中,如果出现以下情况,您将拥有新文件:
List<string> newFiles = hasNewFiles(path, lastKnownFiles);
if (newFiles.Count > 0)
{
processFiles(newFiles);
lastKnownFiles = newFiles;
}
edit: if you want a more linqy solution:
编辑:如果你想要一个更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;
}
回答by Peter
You could use Directory.GetFiles():
你可以使用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
}
}
Note this only checks for new files not changed files, if you need that use FileInfo
请注意,这仅检查新文件而不是更改的文件,如果您需要使用 FileInfo
回答by davisoa
I would question why not to use the FileSystemWatcher. It registers with the OS and is notified immediately when the event finishes in the file system.
我会质疑为什么不使用 FileSystemWatcher。它向操作系统注册,并在文件系统中的事件完成时立即收到通知。
If you really have to poll, then just create a System.Timers.Timer, create a method for it to call, and check for the file in this method.
如果你真的需要轮询,那么只需创建一个 System.Timers.Timer,创建一个方法供它调用,并在该方法中检查文件。
回答by KeithS
Yes, you can create a Timer, and plug a handler into the Elapsed event that will instantiate a DirectoryInfo class for the directory you're watching, and call either GetFiles() or EnumerateFiles(). GetFiles() returns a FileInfo[] array, while EnumerateFiles() returns a "streaming" IEnumerable. EnumerateFiles() will be more efficient if you expect a lot of files to be in that folder when you look; you can start working with the IEnumerable before the method has retrieved all the FileInfos, while GetFiles will make you wait.
是的,您可以创建一个计时器,并将处理程序插入 Elapsed 事件,该事件将为您正在观看的目录实例化 DirectoryInfo 类,并调用 GetFiles() 或 EnumerateFiles()。GetFiles() 返回一个 FileInfo[] 数组,而 EnumerateFiles() 返回一个“流式”IEnumerable。如果您在查看时希望该文件夹中有很多文件,则 EnumerateFiles() 将更有效;您可以在方法检索到所有 FileInfos 之前开始使用 IEnumerable,而 GetFiles 会让您等待。
As to why this may actually be better than FileWatcher, it depends on the architecture behind the scenes. Take, for example, a basic Extract/Transform/Validate/Load workflow. First, such a workflow may have to create expensive instances of objects (DB connections, instances of a rules engine, etc). This one-time overhead is significantly mitigated if the workflow is structured to handle everything available to it in one go. Second, FileWatcher would require anything called by the event handlers, like this workflow, to be thread-safe, since MANY events can be running at once if files are constantly flowing in. If that is not feasible, a Timer can be very easily configured to restrict the system to one running workflow, by having event handlers examine a thread-safe "process running" flag and simply terminate if another handler thread has set it and not yet finished. The files in the folder at that time will be picked up the next time the Timer fires, unlike FileWatcher, where if you terminate the handler the information about the existence of that file is lost.
至于为什么这实际上可能比 FileWatcher 更好,这取决于幕后的架构。以一个基本的提取/转换/验证/加载工作流为例。首先,这样的工作流可能必须创建昂贵的对象实例(数据库连接、规则引擎的实例等)。如果工作流的结构可以一次性处理所有可用的内容,那么这种一次性开销就会大大减轻。其次,FileWatcher 需要事件处理程序调用的任何东西,比如这个工作流,是线程安全的,因为如果文件不断流入,许多事件可以同时运行。如果这不可行,可以很容易地配置一个 Timer通过让事件处理程序检查线程安全的“进程运行”来将系统限制为一个正在运行的工作流 如果另一个处理程序线程设置了它并且尚未完成,则标记并简单地终止。此时文件夹中的文件将在下次触发 Timer 时被拾取,这与 FileWatcher 不同,如果您终止处理程序,有关该文件存在的信息将丢失。
回答by Wildhorn
1) Sounds like your boss is an idiot
2) You will have to use functions like Directory.GetFiles, File.GetLastAccessTime, etc and keep it in memory to check if it changed.
1) 听起来你的老板是个白痴
2) 你必须使用 Directory.GetFiles、File.GetLastAccessTime 等函数并将其保存在内存中以检查它是否已更改。
回答by Brian Gideon
It is a little odd that you cannot use FileSystemWatcheror presumably any of the Win32 APIs that do the same thing, but that is irrelevant at this point. The polling method might look like this.
有点奇怪的是,您不能使用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();
}
}

