Tres problemas con FileSystemWatcher, la primera es que se puede enviar eventos de la creación duplicados para que compruebe de que con algo como:
this.watcher.Created += (s, e) =>
{
if (!this.seen.ContainsKey(e.FullPath)
|| (DateTime.Now - this.seen[e.FullPath]) > this.seenInterval)
{
this.seen[e.FullPath] = DateTime.Now;
ThreadPool.QueueUserWorkItem(
this.WaitForCreatingProcessToCloseFileThenDoStuff, e.FullPath);
}
};
donde this.seen
es una Dictionary<string, DateTime>
y this.seenInterval
es una TimeSpan
.
A continuación, tiene que esperar a que el creador del archivo termine de escribirlo (el problema planteado en la pregunta). Y, tercero, debe tener cuidado porque a veces el evento de creación de archivos se lanza antes de que se pueda abrir el archivo sin darle un FileNotFoundException
, pero también se puede quitar antes de que pueda obtenerlo, lo que también da un FileNotFoundException
.
private void WaitForCreatingProcessToCloseFileThenDoStuff(object threadContext)
{
// Make sure the just-found file is done being
// written by repeatedly attempting to open it
// for exclusive access.
var path = (string)threadContext;
DateTime started = DateTime.Now;
DateTime lastLengthChange = DateTime.Now;
long lastLength = 0;
var noGrowthLimit = new TimeSpan(0, 5, 0);
var notFoundLimit = new TimeSpan(0, 0, 1);
for (int tries = 0;; ++tries)
{
try
{
using (var fileStream = new FileStream(
path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
// Do Stuff
}
break;
}
catch (FileNotFoundException)
{
// Sometimes the file appears before it is there.
if (DateTime.Now - started > notFoundLimit)
{
// Should be there by now
break;
}
}
catch (IOException ex)
{
// mask in severity, customer, and code
var hr = (int)(ex.HResult & 0xA000FFFF);
if (hr != 0x80000020 && hr != 0x80000021)
{
// not a share violation or a lock violation
throw;
}
}
try
{
var fi = new FileInfo(path);
if (fi.Length > lastLength)
{
lastLength = fi.Length;
lastLengthChange = DateTime.Now;
}
}
catch (Exception ex)
{
}
// still locked
if (DateTime.Now - lastLengthChange > noGrowthLimit)
{
// 5 minutes, still locked, no growth.
break;
}
Thread.Sleep(111);
}
Puede, por supuesto, establecer sus propios tiempos de espera. Este código deja suficiente tiempo para un bloqueo de 5 minutos. El código real también tendría una bandera para salir del hilo si así lo solicita.
Tuve el mismo problema, y además FileSystemWatcher no parece funcionar correctamente en Windows 7, a veces no obtengo el evento fireing –