I searched quite a lot on this and found a lot of other questions on this topic which boil down to use a FileSystemWatcher
, and in its Changed
event, open the file for reading with FileShare.None
and check if that throws an exception. However, this only works half the time for me.
I created a simple console application to test this behaviour which essentially boils down to the following:
FileSystemWatcher fsw = new FileSystemWatcher("d:\\locktest");
fsw.Changed += (sender, e) => {
String fp = e.FullPath;
Console.WriteLine(fp + " changed");
try {
using(FileStream s = new FileStream(fp, FileMode.Open, FileAccess.Read, FileShare.None)) { }
Console.WriteLine(fp + " complete");
} catch (IOException ex) {
Console.WriteLine(fp + " not complete");
}
};
fsw.Created += (sender, e) => Console.WriteLine(e.FullPath + " created");
fsw.EnableRaisingEvents = true;
Console.ReadKey(); // would immediately exit otherwise
I now tested this with different files and directories, and here are my results:
Small file (1MiB)
Copied via Windows explorer
d:\locktest\1mb created
d:\locktest\1mb changed
d:\locktest\1mb complete
d:\locktest\1mb changed
d:\locktest\1mb complete
Result: Correct, but twice.
Copied via DOS copy
Same as via Windows explorer.
Copied via Cygwin cp
d:\locktest\1mb created
d:\locktest\1mb changed
d:\locktest\1mb complete
Correct result.
1GiB large file
Copied via Windows explorer
d:\locktest\1g created
d:\locktest\1g changed
d:\locktest\1g not complete
d:\locktest\1g changed
d:\locktest\1g complete
Correct result.
Copied via DOS copy
Same result
Copied via Cygwin cp
d:\locktest\1g created
d:\locktest\1g changed
d:\locktest\1g complete
Also a correct result
Large file (10GiB)
Copied via Windows explorer
d:\locktest\10g created
d:\locktest\10g changed
d:\locktest\10g not complete
d:\locktest\10g changed
d:\locktest\10g not complete
The last "changed" event is triggered 'too early", or better: No "changed" event is triggered when the file is complete
Copied via DOS copy
Same result
Copied via Cygwin cp
d:\locktest\10g created
d:\locktest\10g changed
d:\locktest\10g complete
Correct result.
So, how should I approach a reliable detection if a file written to a watched directory is really "complete", i.e., "usable"?
My current approach would be the following:
- Listen for
Created
andComplete
- Try to open the file there
- If opening the file failed, put this file as a "candidate" into a (synchronized) list of files to be checked again
- Check this list every X seconds
- If
Changed
occurrs on one of the files in the list, remove it from there and immediately check it, re-adding it when it fails again
This involves some sort of polling, and looks a bit "hackish" to me. Plus, for very small files, I get the completed information twice, so I figure I have to track already-complete files and check if they have changed since.
Is this the only way to go or is there a better approach?