FileSystemWatcher seems to ignore multiple dots in filenames

455 views Asked by At

I have a C# windows service watching a folder. It seems like if files are created using the traditional windows file name structure, everything is fine, e.g. foo.zip. But then along comes one of those pesky unix people who name their files: foo.bar.11-10-2013.zip.

File Watcher in my program never sees this file. There is no filter set, so it is defaulting to *.*.

If I rename the file to remove the dots and replace them with underscores, the file watcher sees the file.

I tried googling an answer but my other problem is I'm not sure how to state the question - do we call it multiple extensions or multiple dots or multiple periods? All of these returned unhelpful results.

So my question: Is it possible to set up a file watcher to detect linux style file names with multiple dot extensions on it? And if you happen to know a formal term for "multi-dot file extensions", I'd love to know what they are called.

2

There are 2 answers

5
Bruce Van Horn On

I've decided the answer is that you can't, at least not without a lot of work. I pondered a timer that renames multi-dot files every few seconds, but that seems shabby.

I had the file watcher set to fire when files are created - which is what you get when you drag the control on to the design surface.

I changed the event to detect changes, and I filtered on size. It seems that . precludes "multi-dot" file extensions, and why wouldn't it? foo.bar.blah.zip doesn't fit the . pattern. What we need are ant-like file pattern descriptors (since nobody offered up a better term :-), but those don't seem to be supported via the documentation.

By having it watch for changes in file sizes seems to fire when files are created (going from zero to something) which is good enough for me. Once I know the file is there, I can get its name regardless of the control's filter.

0
Bruce Van Horn On

The answer I posted last night didn't pass regression testing. I got mixed results and file system watcher's different filter settings, and it occasionally missed files which is unacceptable. There are lots of articles on problems with network shares but I take that to mean the watcher is watching a network share mapped to a different computer, not that the directory being watched is itself a network share on the same machine where the service is running. It is possible latency is a factor in some of my mis-fires, but even locally, the component does not seem to recognize multi-dot files names.

Since this is a service, we already had a method to detect any files already present when the service started. This method worked and had no reliance on the component. So the most elegant solution was to simply put that code on a timer. Below are the relevant parts of the class (i.e. this snippet isn't designed to be copy / paste ready, only to show how I solved the problem). Don't let the FTP moniker throw you off course - it is really just watching a shared folder which might or might not be mapped to an FTP server.

using System.Collections.Generic;
using Timer = System.Timers.Timer;

public partial class VsiFtpManager : ServiceBase
{
    private Timer _searchTimer;
    private Queue<string> _filesToProcess;
    private string _ftpRoot; //this is set elsewhere from the registry

    protected override void OnStart(string[] args)
    {

        //process any files that are already there when the service starts
        LoadExistingFtpFiles(); 

        //Handle new files
        _searchTimer = new Timer(10000);
        _searchTimer.Elapsed += LoadExistingFtpFiles;
    }

    //convenience overload to allow this to handle timer events
    private void LoadExistingFtpFiles(object source, ElapsedEventArgs evtArgs)
    {
        LoadExistingFtpFiles();
    }

    private void LoadExistingFtpFiles()
    {
        _searchTimer.Stop();
        var di = new DirectoryInfo(_ftpRoot);
        FileInfo[] fileInfos = di.GetFiles("*.*", SearchOption.AllDirectories);
        foreach (FileInfo fi in fileInfos.Where(fi => fi != null))
        {
            if (fi.Extension != "processed" && !_filesToProcess.Contains(fi.FullName))
            {
                LogHelper.BroadcastLogMessage("INFO:  File " + fi.Name + " was uploaded.", EventLogEntryType.Information);
                _filesToProcess.Enqueue(fi.FullName);
                LogHelper.BroadcastLogMessage("File received: " + fi.Name, EventLogEntryType.Information);
            }
        }
        _searchTimer.Start();

    }
}

The part you don't see, which is beyond the scope of my question, is essentially a co-routine running against the queue _filesToProcess which processes the files then renames them to have an extension of .processed.

So my final answer: My research, borne out by automated regression testing, showed file system watcher to be unreliable for my use case, which requires me to process files copied into a folder. Some of these files will come from Unix systems and so may have non-windows file names. The file system watcher component shipping with .net cannot reliably detect unix style files names bearing multiple dots within the name.

I replaced the file system watcher with a simple polling mechanism. The execution is significantly slower but reliable, which is my main objective. The overall solution reduced my lines of code, albeit insignificantly, and removed my only component on the design surface of the service, both of which I consider bonuses owing to my own possibly peculiar preferences.