Update: a few people have questioned why I did not use the FileSystemWatcher class. If you do NOT need to check for the EXISTENCE (different than the CREATION) of a file in a particular directory and you do NOT need RegEx patterns, then FSW would be more efficient. If, however, like me, you need to look for the creation of a file in a particular directory via a more fail-safe approach (i.e.: to still pick up on files created even when the launching process errored or exited unexpectedly), you will need something like what I have coded, below.
What is a Directory Poller?
A directory poller is a process that monitors a file system directory for the existence of any files that meet a particular filename pattern and does something accordingly.
When the Directory Poller finds a file that matches, what does it do?
Being that this is a generic utility class, it does pretty much what you want it to do. Just pass in a reference to a method (or pass an inline anonymous method) that matches the delegate
ProcessFile's signature. This method will execute once for every file in the directory whose filename matches your specified pattern.
Show me a practical example!
Let's say that you need to write an application that monitors a directory for text files with the naming pattern of "data1.txt", "data2.txt", etc. and imports the data from those files into a database. As such, you would've had to write a class whose method reads the file into the database and on completion removes it from the directory. Let's say its signature was a static method
DBUtils.ImportText(string filePath). In order "wire up" this method to the DirectoryPoller class, you would write the code like this:
DirectoryPoller dp = new DirectoryPoller();
dp.PollingDirectory = @"c:\temp\poll\";
dp.AddFileProcess("data[0-9]+", DBUtils.ImportText(filePath));
dp.Poll();The 2 arguments to the
AddFileProcess method are 1) a string that represents a
Regular Expression for specifying the filename pattern and 2) a delegate (
ProcessFile) that accepts a reference to the method you want the DirectoryPoller to run. When the
Poll() method is called, that referenced method (known as a delegate) you supplied is run for each file found that matches the Regular Expression pattern you also supplied. Typically, the
Poll() method would be called in a process that runs periodically.
Note: you may add multiple file processes to the DirectoryPoller, i.e.: multiple calls to the AddFileProcess() method are allowed. The DirectoryPoller will only match a file once, even if multiple filename patterns apply.
You mentioned that an anonymous method could be used as well?
Yes, if you need to reference a fairly simple method to be run by the DirectoryPoller, you can use an anonymous method in an inline fashion. For instance, to simply display the name of the files that the DirectoryPoller finds, you would use the following code:
DirectoryPoller dp = new DirectoryPoller();
dp.PollingDirectory = @"c:\temp\poll\";
dp.AddFileProcess("data[0-9]+", delegate(string filePath)
{ Console.Out.WriteLine("found file: " + filePath); });
dp.Poll();If you are using .NET 3.5. you can use a
lambda expression in the place of the anonymous method, like this:
dp.AddFileProcess("data[0-9]+", filePath => { Console.Out.WriteLine("found file: " + filePath); });
Show me the DirectoryPoller code!
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace Utility
{
public delegate void ProcessFile(string filePath);
public class DirectoryPoller
{
private string _pollingDirectory = String.Empty;
private Dictionary _fileProcesses = new Dictionary();
public string PollingDirectory
{
get
{
return _pollingDirectory;
}
set
{
_pollingDirectory = value;
}
}
public void Poll()
{
if (_fileProcesses != null && _fileProcesses.Count > 0)
{
if (Directory.Exists(_pollingDirectory))
{
foreach (string path in Directory.GetFiles(_pollingDirectory, "*.*",
SearchOption.TopDirectoryOnly))
{
foreach (string fileRegEx in _fileProcesses.Keys)
{
if (Regex.IsMatch(Path.GetFileName(path), fileRegEx)) //filename matches
{
_fileProcesses[fileRegEx].Invoke(path); //invoke delegate
break; //match only on one RegEx
}
}
}
}
else
{
throw new ApplicationException(String.Format("Directory {0} not found.",
_pollingDirectory));
}
}
}
public void AddFileProcess(string fileRegEx, ProcessFile processMethod)
{
_fileProcesses.Add(fileRegEx, processMethod);
}
}
}