Raminder
Raminder

Reputation: 1887

Filewatcher created event

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?

Upvotes: 3

Views: 2113

Answers (8)

Hath
Hath

Reputation: 12769

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);
    }
}

Upvotes: 3

Aaron Fischer
Aaron Fischer

Reputation: 21211

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.

Upvotes: 2

Winston Smith
Winston Smith

Reputation: 21884

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.

Upvotes: 2

oglester
oglester

Reputation: 6655

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.

Upvotes: 0

hova
hova

Reputation: 2841

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.

Upvotes: 1

Ricardo Villamil
Ricardo Villamil

Reputation: 5107

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!

Upvotes: 1

h0st1le
h0st1le

Reputation: 98

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;
        }
    }

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1499790

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).

Upvotes: 2

Related Questions