tags:

views:

560

answers:

9

I am monitoring a folder with a .net filewatcher for certain kind of files(*.mbxml). I am using the created event of filewatcher for it. Once the created event fires I have to move this file to another folder. The problem with this approach is that the created event is fired as soon as the file copying starts. So if the file is taking too long to copy to the folder being watched, the code that moves the file fails. I've searched and the only solution I found on the net was that you move the file within a try-catch block and keep trying until the whole file is copied. I don't like this solution, it would've been better if the created event was fired once the whole file had finished copying or there was a separate event for it. Is there another way of achieving this?

+1  A: 

Are you able to change the program which is creating the files? If so, change it to create them in one folder and then move them (atomically) to a different folder - or use a different extension (anything you can track, really).

Jon Skeet
+2  A: 

i have yet to see this done in anyway except with a try catch block. usually on the oncreate event i set a bool that try's to open the file. this determines if the rest of the code proceeds, if there is another way i would be interested as well.

    private static bool creationComplete(string fileName)
    {
        // if the file can be opened it is no longer locked and now available
        try
        {
            using (FileStream inputStream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                return true;
            }
        }
        catch (IOException)
        {
            return false;
        }
    }
artifact of culture war
+1  A: 

Have you tried moving the file using BinaryReader and BinaryWriter? You can probably read the file as it's being copied and write it a a slower pace so your move doesn't beat the speed at which the other process creates the file. You could also insert some wait time in between reads. Once you make the last read and verify the file creation is finished you can then delete it.

Good luck!

Ricardo Villamil
+1  A: 

The whole reason a try/catch solution is used, is because the only way to know if the other program is finished writing to the file is for the file open() call to SUCCEED. That is a FAILURE is the definition of the file still being written to.

Typically in your situation a while() loop is used with a thread.sleep timer in it, which keeps trying until either A) timeout or B) success.

hova
A: 

If I recall correctly, the FileWatcher raises several events per IO on a given file/folder. You may be able to narrow down the FileSystemEventArgs associated with completion. It may be necessary to defer to checking if the file is accessible, but I would think it a less desirable solution.

Greg Ogle
+1  A: 

From: http://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher.aspx

"Common file system operations might raise more than one event. For example, when a file is moved from one directory to another, several OnChanged and some OnCreated and OnDeleted events might be raised. Moving a file is a complex operation that consists of multiple simple operations, therefore raising multiple events. Likewise, some applications (for example, antivirus software) might cause additional file system events that are detected by FileSystemWatcher. "

How time sensitive are your requirements for moving the file?

If you don't like the idea of trying to move it, catching exceptions, and repeating until you get a success you could take a slightly different approach.

How about once you receive a Created event, you begin monitoring the file's last write time. Once a certain time has passed since the last write time, say two seconds, you can try moving the file then. You'll probably find there a lot less 'failed' moves, but the end result will be the same.

Winston Smith
+1  A: 

Watch for the on created event but wait for the last write time change event. When the file is done being copied the last write time is updated. Since you are moving the files, you can probably just handle the last write time change event.

Aaron Fischer
+1  A: 

you could make your code simpler by just inheriting from FileSystemWatcher and fireing your own FileReady Event, like so:

public class CustomFileSystemWatcher : System.IO.FileSystemWatcher
{
    public CustomFileSystemWatcher()
    {
        this.Created += new FileSystemEventHandler(CustomFileSystemWatcher_Created);
    }


    private void CustomFileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {
        ThreadPool.QueueUserWorkItem((n) => { WaitFileReady(e); });
    }

    private void WaitFileReady(FileSystemEventArgs e)
    {
        while (true)
        {
            try
            {
                using (FileStream fs = File.Open(e.FullPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                {
                    //exit
                    break;
                }
            }
            catch (Exception)
            {
                //wait if you like
                Thread.Sleep(100);
            }
        }
        OnFileReady(e);
    }

    public event FileSystemEventHandler FileReady;

    protected virtual void OnFileReady(FileSystemEventArgs e)
    {
        if (this.EnableRaisingEvents && FileReady != null) FileReady(this, e);
    }
}
Hath
Your code is basically the same approach, you've just written the code in your inherited class.
Raminder
A: 

This is great answer. This has solved my problem. Thanks a lot.

shafqat ali