The FileSystemWatcher control allows you to easily monitor a directory or set of directories. It raises events when files are added, deleted and updated and lets you perform actions cooresponding to those events.
At work, I have an application that allows users to upload image files to one of our web servers to be used on our web site. For the longest time now, someone has been manually copying those images to the rest of the servers on our web farm.
The first thing I know you are going to say is, "Do you know they have something called files replication?"
Let me just say...
1) I do know what file replication is.
2) It would have been a lot easier to use file replication to accomplish this.
However, I don't want to waste your time as to why the operations guys at my company wont use file replication. Hint...they deleted all the files off one of our servers once, and now they are scared of it. Oops, was that my outloud voice? :-o
I decided to create a service that would constantly monitor the one server and copy the new and updated files to the other servers. Honestly, the service only took me about 30 minutes to write, and that was with taking time to to figure out how to use the FileSystemWatcher control and remembering how to install and debug a service.
First I created a new C# Windows Service.
Next I created a configuration file for the service that had a setting for the source server and the targetservers. The contents of the file looked like:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<sourceserver name="name of server to copy from" path="path on the server to monitor"/>
<targetserver name="name of server to copy files to" path="path on server to copy to"/>
</configuration>
You can set as many targetservers as you like.
After Visual Studio created the files for the project I only had to make a few edits to the code. First I had to add two private variables to the FileWatcher class:
private XmlDocument Config;
private System.Collections.Hashtable TargetServers;
private FileSystemWatcher FileWatcher;
Next I added this code when the service starts:
this.FileWatcher.EnableRaisingEvents = true;
this.FileWatcher.Changed += new System.IO.FileSystemEventHandler(this.FileWatcher_Changed);
this.FileWatcher.Created += new System.IO.FileSystemEventHandler(this.FileWatcher_Changed);
Since I want to perform the same action when a file is changed or updated, I set the event handlers of both events to call the same function.
After that, I updates the OnStart function to look like this:
protected override void OnStart(string[] args)
{
Config = new XmlDocument();
TargetServers = new Hashtable();
string localpath = System.IO.Path.GetDirectoryName(
System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase );
//load the config file
try
{
Config.Load(localpath+"\\config.xml");
}
catch (System.Xml.XmlException xex)
{
this.EventLog.WriteEntry("Could not open config file. Error was:\n"+xex.Message,System.Diagnostics.EventLogEntryType.Error,1,1);
}
//set the path that the filesystemwatcher will watch
FileWatcher.Path = Config.SelectSingleNode("configuration/sourceserver/@path").Value;
//fill the TargetServers Hashtable with all the target servers that we will copy the files to.
foreach (XmlNode node in Config.SelectNodes("configuration/targetserver"))
{
TargetServers.Add(node.SelectSingleNode("@name").Value,node.SelectSingleNode("@path").Value);
}
//set the file properties filesytemwatcher uses to detect changes to files
FileWatcher.NotifyFilter=NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size;
FileWatcher.IncludeSubdirectories=false;
}
Finally, I added the function to execute for the FileWatcher_Changed event:
private void FileWatcher_Changed(object sender, System.IO.FileSystemEventArgs e)
{
IDictionaryEnumerator ListEnumerator = TargetServers.GetEnumerator();
try
{
switch (e.ChangeType)
{
case WatcherChangeTypes.Created:
while (ListEnumerator.MoveNext())
{
File.Copy(e.FullPath, ListEnumerator.Value.ToString()+"\\"+e.Name, true);
}
break;
case WatcherChangeTypes.Changed:
while (ListEnumerator.MoveNext())
{
File.Copy(e.FullPath, ListEnumerator.Value.ToString()+"\\"+e.Name, true);
}
break;
}
}
catch (IOException ix)
{
this.EventLog.WriteEntry("IOException while copying files. Error was:\n"+ix.Message,System.Diagnostics.EventLogEntryType.Error,1,1);
}
catch (Exception x)
{
this.EventLog.WriteEntry("Unknown Exception while copying files. Error was:\n"+x.Message,System.Diagnostics.EventLogEntryType.Error,1,1);
}
}
The one last thing I needed to do was add the installer. Just right click on the designer for the services class files and select Create Installer. Save and build the Solution.
To install the service, run the following in the directory where the exe for the service was created:
installutil filewatcher.exe
You can then start the service either in the Services Control Panel.
If you need to debug the service, you can attach to the process and then set breakpoints as long as you have created a debug build.
References I used while writing my code:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vborireactingtofilesystemevents.asp