2010-08-31 10 views
7

Tengo que crear un servicio de Windows que supervisa una carpeta específica para nuevos archivos y la procesa y la mueve a otra ubicación.Supervisión de un directorio para la creación de archivos nuevos sin FileSystemWatcher

Empecé con el uso de FileSystemWatcher. A mi jefe no le gusta FileSystemWatcher y quiere que use sondeos usando Timer o cualquier otro mecanismo que no sea FileSystemWatcher.

¿Cómo se puede controlar la dirección sin usar FileSystemWatcher usando .NET framework?

+5

Tal vez esto una prueba de cuándo debe luchar contra su jefe opinión ... Sustitución con un mecanismo de sondeo suena como una locura hablar – fletcher

+7

Por qué no funciona ¿Tu jefe quiere que uses FileSystem Watcher? La razón podría apuntar hacia la mejor solución. Si no hay una razón, @Giorgi probablemente tenga la respuesta correcta. – Kendrick

+3

Supuestamente, FileSystemWatcher no funciona en unidades de red, y yo personalmente experimento casos en los que no se desencadena. –

Respuesta

6

Usando la respuesta de @ Petoj, he incluido un servicio completo de Windows que sondea cada cinco minutos para nuevos archivos. Está contratado, por lo que solo un hilo sondea, toma en cuenta el tiempo de procesamiento y admite la pausa y la detención oportuna. También es compatible con la atadura fácil de un debbugger en system.start

public partial class Service : ServiceBase{ 


    List<string> fileList = new List<string>(); 

    System.Timers.Timer timer; 


    public Service() 
    { 
     timer = new System.Timers.Timer(); 
     //When autoreset is True there are reentrancy problems. 
     timer.AutoReset = false; 

     timer.Elapsed += new System.Timers.ElapsedEventHandler(DoStuff); 
    } 


    private void DoStuff(object sender, System.Timers.ElapsedEventArgs e) 
    { 
     LastChecked = DateTime.Now; 

     string[] files = System.IO.Directory.GetFiles("c:\\", "*", System.IO.SearchOption.AllDirectories); 

     foreach (string file in files) 
     { 
      if (!fileList.Contains(file)) 
      { 
       fileList.Add(file); 

       do_some_processing(); 
      } 
     } 


     TimeSpan ts = DateTime.Now.Subtract(LastChecked); 
     TimeSpan MaxWaitTime = TimeSpan.FromMinutes(5); 

     if (MaxWaitTime.Subtract(ts).CompareTo(TimeSpan.Zero) > -1) 
      timer.Interval = MaxWaitTime.Subtract(ts).TotalMilliseconds; 
     else 
      timer.Interval = 1; 

     timer.Start(); 
    } 

    protected override void OnPause() 
    { 
     base.OnPause(); 
     this.timer.Stop(); 
    } 

    protected override void OnContinue() 
    { 
     base.OnContinue(); 
     this.timer.Interval = 1; 
     this.timer.Start(); 
    } 

    protected override void OnStop() 
    { 
     base.OnStop(); 
     this.timer.Stop(); 
    } 

    protected override void OnStart(string[] args) 
    { 
     foreach (string arg in args) 
     { 
      if (arg == "DEBUG_SERVICE") 
        DebugMode(); 

     } 

     #if DEBUG 
      DebugMode(); 
     #endif 

     timer.Interval = 1; 
     timer.Start(); 
    } 

    private static void DebugMode() 
    { 
     Debugger.Break(); 
    } 

} 
+1

Gracias por su respuesta. Estoy confundido acerca de cómo funciona el temporizador. En el "do_some_processing" tengo compresión y cifrado de archivos de gran tamaño y también operaciones de bases de datos. Ambos requieren bastante tiempo. Lo que sucede cuando la operación dice que lleva más de (solo imaginar) 5 minutos (el intervalo de sondeo). Supongamos que el procesamiento duró 6 minutos, entonces, si estoy entendiendo su código correctamente, permitirá un procesamiento de 6 minutos y luego comenzará el temporizador. Antes de ver el código, estaba usando un temporizador con un intervalo de 30 segundos y confundí lo que ocurre cuando el temporizador transcurre en medio del procesamiento – Abbi

+0

Gracias, Conrad, finalmente pude crear mi servicio y su respuesta fue muy útil. – Abbi

+2

Al código le falta la declaración de la variable 'LastChecked' de toda la clase. – hofnarwillie

1

Me pregunto por qué no utilizar el FileSystemWatcher. Se registra con el SO y se notifica inmediatamente cuando el evento finaliza en el sistema de archivos.

Si realmente tiene que sondear, simplemente cree un Temporizador System.Timers.Timer, cree un método para que llame y verifique el archivo con este método.

4

Usted podría utilizar Directory.GetFiles():

using System.IO; 

var fileList = new List<string>(); 

foreach (var file in Directory.GetFiles(@"c:\", "*", SearchOption.AllDirectories)) 
{ 
    if (!fileList.Contains(file)) 
    { 
     fileList.Add(file); 
     //do something 
    } 
} 

recordar este comprueba solamente para los nuevos archivos no los archivos modificados, si es necesario que el uso FileInfo

+0

Gracias Petoj, tu respuesta me dio la ventaja que necesitaba. – Abbi

+0

cool +1? o respuesta? – Peter

+0

Me gustaría hacer +1 pero no tengo suficiente reputación o soy muy nuevo en este sitio porque no sé cómo hacerlo. – Abbi

0

1) Parece que su jefe es un idiota
2) Tendrá que usar funciones como Directory.GetFiles, File.GetLastAccessTime, etc. y guárdelo en la memoria para comprobar si ha cambiado.

+0

+1 para el número dos. –

0

Es un poco extraño que no pueda usar FileSystemWatcher o presumiblemente cualquiera de las API de Win32 que hacen lo mismo, pero eso es irrelevante en este momento. El método de votación podría verse así.

public class WorseFileSystemWatcher : IDisposable 
{ 
    private ManaulResetEvent m_Stop = new ManaulResetEvent(false); 

    public event EventHandler Change; 

    public WorseFileSystemWatcher(TimeSpan pollingInterval) 
    { 
    var thread = new Thread(
    () => 
     { 
     while (!m_Stop.WaitOne(pollingInterval)) 
     { 
      // Add your code to check for changes here. 
      if (/* change detected */) 
      { 
      if (Change != null) 
      { 
       Change(this, new EventArgs()) 
      } 
      } 
     } 
     }); 
    thread.Start(); 
    } 

    public void Dispose() 
    { 
    m_Stop.Set(); 
    } 
} 
+0

¡Me encanta el nombre! jaja! –

17

En realidad, el componente FileWatcher no es 100% "estable" de mi experiencia a lo largo de los años. Inserta suficientes archivos en una carpeta y perderás algunos eventos. Esto es especialmente cierto si supervisa un recurso compartido de archivos, incluso si aumenta el tamaño del búfer.

Por lo tanto, por razones prácticas, utilice FileWatcher junto con un temporizador que escanea una carpeta para ver los cambios, para obtener la solución más óptima.

Los ejemplos de crear código de temporizador deberían ser abundantes si lo busca en google. Si realiza un seguimiento del último DateTime cuando se ejecutó el temporizador, luego verifique la fecha de modificación de cada archivo y compárelo con la fecha. Lógica bastante simple.

El intervalo del temporizador depende de cuán urgentes sean los cambios para su sistema. Pero verificar cada minuto debería estar bien para muchos escenarios.

+4

+1 Esta es la solución correcta: FileWatcher plus polling como respaldo para manejar lo que FileWatcher falla. -1 todos los que piensan que FileWatcher es confiable. –

+9

La mayoría de los problemas de confiabilidad de 'FileSystemWatcher' provienen de un malentendido acerca de cómo funciona. El evento 'Changed' no se activa cuando se pone en cola una escritura en el disco, sino que se genera solo después de que se ha confirmado la escritura. La caché de disco detrás de la grabación puede afectar la puntualidad de los eventos al retrasarlos indefinidamente. La solución informada es eliminar el caché mediante 'FlushFileBuffers'. Sospecho que hay otros problemas con los recursos compartidos de red que tienen un impacto negativo en la confiabilidad. –

+1

@Brian: Muy buena explicación, y no he pensado en la memoria caché de escritura. Para los recursos compartidos de red, es el tamaño del búfer del tráfico smb, que es demasiado pequeño para transferir todos los eventos, por lo que algunos podrían perderse. –

4

Al iniciar el programa, use Directory.GetFiles (ruta) para obtener la lista de archivos.

A continuación, crear un contador de tiempo, y en sus llamadas hasNewFiles evento transcurrido:

static List<string> hasNewFiles(string path, List<string> lastKnownFiles) 
    { 
     List<string> files = Directory.GetFiles(path).ToList(); 
     List<string> newFiles = new List<string>(); 

     foreach (string s in files) 
     { 
      if (!lastKnownFiles.Contains(s)) 
       newFiles.Add(s); 
     } 

     return new List<string>(); 
    } 

En el código de llamada, tendrá nuevos archivos si:

List<string> newFiles = hasNewFiles(path, lastKnownFiles); 
    if (newFiles.Count > 0) 
    { 
     processFiles(newFiles); 
     lastKnownFiles = newFiles; 
    } 

edición: si quieres una más solución linqy:

static IEnumerable<string> hasNewFiles(string path, List<string> lastKnownFiles) 
    { 
     return from f in Directory.GetFiles(path) 
       where !lastKnownFiles.Contains(f) 
       select f; 
    } 

    List<string> newFiles = hasNewFiles(path, lastKnownFiles); 
    if (newFiles.Count() > 0) 
    { 
     processFiles(newFiles); 
     lastKnownFiles = newFiles; 
    } 
+0

Gracias SnOrfus. Tu respuesta fue muy útil. – Abbi

+0

@Abbi: De nada. –

1

Sí, se puede crear un temporizador, y conecte un controlador en el caso de que transcurrido instanti comió una clase de DirectoryInfo para el directorio que está viendo y llama a GetFiles() o EnumerateFiles(). GetFiles() devuelve una matriz FileInfo [], mientras que EnumerateFiles() devuelve una IEnumerable de "transmisión". EnumerateFiles() será más eficiente si esperas que haya muchos archivos en esa carpeta cuando mires; puede comenzar a trabajar con IEnumerable antes de que el método haya recuperado todos los FileInfos, mientras que GetFiles lo hará esperar.

En cuanto a por qué esto en realidad puede ser mejor que FileWatcher, depende de la arquitectura detrás de las escenas. Tome, por ejemplo, un flujo de trabajo básico de Extraer/Transformar/Validar/Cargar. Primero, tal flujo de trabajo puede tener que crear instancias costosas de objetos (conexiones de DB, instancias de un motor de reglas, etc.). Esta sobrecarga por única vez se mitiga significativamente si el flujo de trabajo está estructurado para manejar todo lo disponible de una sola vez. Segundo, FileWatcher requeriría que cualquier cosa que los manejadores de eventos llamaran, como este flujo de trabajo, sea seguro para subprocesos, ya que muchos eventos se pueden ejecutar a la vez si los archivos fluyen constantemente. Si eso no es factible, un temporizador puede configurarse fácilmente para restringir el sistema a un flujo de trabajo en ejecución, haciendo que los controladores de eventos examinen un indicador de "proceso en ejecución" seguro para subprocesos y simplemente finalice si otro hilo de controlador lo ha establecido y aún no ha finalizado. Los archivos en la carpeta en ese momento serán recogidos la próxima vez que se active el temporizador, a diferencia de FileWatcher, donde si finaliza el controlador la información sobre la existencia de ese archivo se pierde.

Cuestiones relacionadas