2009-08-14 11 views
9

¿Cómo puedo implementar un FileSystemWatcher para una ubicación de FTP (en C#). La idea es que cada vez que se agrega algo en la ubicación de FTP, deseo copiarlo a mi máquina local. Cualquier idea será de ayuda.FileSystemWatcher para FTP

Este es un seguimiento de mi pregunta anterior Selective FTP download using .NET.

+0

Va a utilizar el enfoque de tipo de sondeo. Debe verificar el sitio ftp periódicamente para verificar si hay un nuevo archivo. – jersoft

Respuesta

14

Va a tener que implementar una solución de sondeo, donde sigue pidiendo el contenido del directorio periódicamente. Compare esto con una lista en caché de la llamada anterior y determine qué sucedió de esa manera.

No hay nada en el protocolo FTP que lo ayude con esto desafortunadamente.

3

Escriba un servicio simple para crear FileSystemWatcher, apuntando a su ubicación ftp.

Luego, cuando se carga o modifica un archivo, se activará un evento en su servicio, que luego podrá usar para copiar el archivo a su máquina local.
File.Copy etc.

Hav un vistazo a: this blog

+0

¿'FileSystemWatcher' funciona con URL? –

+0

Ubicaciones UNC, no URL's. – Bravax

7

Los trabajos de clase FileSystemWatcher mediante el registro de eventos con el sistema operativo Windows de acogida. Como tal, se limita a trabajar en rutas locales y rutas UNC a directorios alojados en sistemas Windows. La documentación de MSDN en FileSystemWatcher explica las rutas que puede usar y algunos de los problemas potenciales con el uso de la clase.

Si desea que se lo avise de los cambios en un sitio FTP, tendrá que usar un mecanismo de sondeo para solicitar el estado actual de los archivos o carpetas que le interesa monitorear. Podrá ver cuándo se agregan y eliminan los archivos al comparar las instantáneas del sitio FTP con los cambios y generar eventos similares cuando detecta cambios. Desafortunadamente no podrá detectar el cambio de nombre de eventos, pero otros cambios deberían ser simples de monitorear de esta manera.

0

La forma en que manejo esto es subir una matriz de bytes de un elemento, llamada ".ftpComplete". El FileSystemWatcher solo busca archivos ".ftpComplete" y los quita del final para conocer el archivo real cargado. Dado que el archivo ".ftpComplete" es de sólo 1 byte, que sube casi tan rápido como se crea en el servidor FTP, por lo que puede ser borrado una vez que usted hace lo que sea necesario con el fichero principal

 FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(
      FTPAddress + "/" + Path.GetFileName(filePath) + ".ftpComplete"); 
     request.Method = WebRequestMethods.Ftp.UploadFile; 
     request.Credentials = new NetworkCredential(username, password); 
     request.UsePassive = true; 
     request.UseBinary = true; 
     request.KeepAlive = false; 
     byte[] buffer = new byte[1]; 
     Stream reqStream = request.GetRequestStream(); 
     reqStream.Write(buffer, 0, buffer.Length); 
     reqStream.Close(); 
2

puede supervisar la ubicación FTP siguiente método:

public class FtpFileSystemWatcher 
{ 

    public bool IsRunning 
    { 
     get; 
     private set; 
    } 
    public string FtpUserName 
    { 
     get; 
     set; 
    } 
    public string FtpPassword 
    { 
     get; 
     set; 
    } 
    public string FtpLocationToWatch 
    { 
     get; 
     set; 
    } 
    public string DownloadTo 
    { 
     get; 
     set; 
    } 
    public bool KeepOrignal 
    { 
     get; 
     set; 
    } 
    public bool OverwriteExisting 
    { 
     get; 
     set; 
    } 
    public int RecheckIntervalInSeconds 
    { 
     get; 
     set; 
    } 
    private bool DownloadInprogress 
    { 
     get; 
     set; 
    } 

    private System.Timers.Timer JobProcessor; 

    public FtpFileSystemWatcher(string FtpLocationToWatch = "", string DownloadTo = "", int RecheckIntervalInSeconds = 1, string UserName = "", string Password = "", bool KeepOrignal = false, bool OverwriteExisting = false) 
    { 
     this.FtpUserName = UserName; 
     this.FtpPassword = Password; 
     this.FtpLocationToWatch = FtpLocationToWatch; 
     this.DownloadTo = DownloadTo; 
     this.KeepOrignal = KeepOrignal; 
     this.RecheckIntervalInSeconds = RecheckIntervalInSeconds; 
     this.OverwriteExisting = OverwriteExisting; 

     if (this.RecheckIntervalInSeconds < 1) this.RecheckIntervalInSeconds = 1; 
    } 

    public void StartDownloading() 
    { 

     JobProcessor = new Timer(this.RecheckIntervalInSeconds * 1000); 
     JobProcessor.AutoReset = false; 
     JobProcessor.Enabled = false; 
     JobProcessor.Elapsed += (sender, e) => 
     { 
      try 
      { 

       this.IsRunning = true; 

       string[] FilesList = GetFilesList(this.FtpLocationToWatch, this.FtpUserName, this.FtpPassword); 

       if (FilesList == null || FilesList.Length < 1) 
       { 
        return; 
       } 

       foreach (string FileName in FilesList) 
       { 
        if (!string.IsNullOrWhiteSpace(FileName)) 
        { 
         DownloadFile(this.FtpLocationToWatch, this.DownloadTo, FileName.Trim(), this.FtpUserName, this.FtpPassword, this.OverwriteExisting); 

         if (!this.KeepOrignal) 
         { 
          DeleteFile(Path.Combine(this.FtpLocationToWatch, FileName.Trim()), this.FtpUserName, this.FtpPassword); 
         } 
        } 
       } 

       this.IsRunning = false; 
       JobProcessor.Enabled = true;      
      } 

      catch (Exception exp) 
      { 
       this.IsRunning = false; 
       JobProcessor.Enabled = true; 
       Console.WriteLine(exp.Message); 
      } 
     }; 

     JobProcessor.Start(); 
    } 

    public void StopDownloading() 
    { 
     try 
     { 
      this.JobProcessor.Dispose(); 
      this.IsRunning = false; 
     } 
     catch { } 
    } 

    private void DeleteFile(string FtpFilePath, string UserName, string Password) 
    { 
     FtpWebRequest FtpRequest; 
     FtpRequest = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFilePath)); 
     FtpRequest.UseBinary = true; 
     FtpRequest.Method = WebRequestMethods.Ftp.DeleteFile; 

     FtpRequest.Credentials = new NetworkCredential(UserName, Password); 
     FtpWebResponse response = (FtpWebResponse)FtpRequest.GetResponse(); 
     response.Close(); 

    } 
    private void DownloadFile(string FtpLocation, string FileSystemLocation, string FileName, string UserName, string Password, bool OverwriteExisting) 
    { 
     try 
     { 
      const int BufferSize = 2048; 
      byte[] Buffer = new byte[BufferSize]; 

      FtpWebRequest Request; 
      FtpWebResponse Response; 

      if (File.Exists(Path.Combine(FileSystemLocation, FileName))) 
      { 
       if (OverwriteExisting) 
       { 
        File.Delete(Path.Combine(FileSystemLocation, FileName)); 
       } 
       else 
       { 
        Console.WriteLine(string.Format("File {0} already exist.", FileName)); 
        return; 
       } 
      } 

      Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(Path.Combine(FtpLocation, FileName))); 
      Request.Credentials = new NetworkCredential(UserName, Password); 
      Request.Proxy = null; 
      Request.Method = WebRequestMethods.Ftp.DownloadFile; 
      Request.UseBinary = true; 

      Response = (FtpWebResponse)Request.GetResponse(); 

      using (Stream s = Response.GetResponseStream()) 
      { 
       using (FileStream fs = new FileStream(Path.Combine(FileSystemLocation, FileName), FileMode.CreateNew, FileAccess.ReadWrite)) 
       { 
        while (s.Read(Buffer, 0, BufferSize) != -1) 
        { 
         fs.Write(Buffer, 0, BufferSize); 
        } 
       } 
      } 
     } 
     catch { } 

    } 
    private string[] GetFilesList(string FtpFolderPath, string UserName, string Password) 
    { 
     try 
     { 
      FtpWebRequest Request; 
      FtpWebResponse Response; 

      Request = (FtpWebRequest)FtpWebRequest.Create(new Uri(FtpFolderPath)); 
      Request.Credentials = new NetworkCredential(UserName, Password); 
      Request.Proxy = null; 
      Request.Method = WebRequestMethods.Ftp.ListDirectory; 
      Request.UseBinary = true; 

      Response = (FtpWebResponse)Request.GetResponse(); 
      StreamReader reader = new StreamReader(Response.GetResponseStream()); 
      string Data = reader.ReadToEnd(); 

      return Data.Split('\n'); 
     } 
     catch 
     { 
      return null; 
     } 
    } 


} 
0

Se puede usar un script de Robo-FTP para supervisar el sitio FTP para los cambios. Aquí hay un enlace a una secuencia de comandos de muestra que envía un correo electrónico cada vez que se detecta un cambio: http://kb.robo-ftp.com/script_library/show/40

Miré la pregunta anterior que enlazó. Creo que debería poder modificar el ejemplo de Robo-FTP y usar el comando SETLEFT con la opción/split para hacer que analice el nombre de la carpeta y el número de archivo ISO del archivo modificado y luego mueva el archivo a la ubicación correcta.

4

No puede usar el FileSystemWatcher o de otra forma, porque el protocolo FTP no tiene ninguna API para notificar a un cliente sobre cambios en el directorio remoto.

Todo lo que puede hacer es iterar periódicamente el árbol remoto y buscar cambios.

En realidad es bastante fácil de implementar, si utiliza una biblioteca cliente FTP que admita la enumeración recursiva de un árbol remoto. Desafortunadamente, el cliente de FTP .NET incorporado, el FtpWebRequest no. Pero, por ejemplo, con WinSCP .NET assembly versión 5.9 (o más reciente), puede usar el Session.EnumerateRemoteFiles method.

vea el artículo Watching for changes in SFTP/FTP server:

// Setup session options 
SessionOptions sessionOptions = new SessionOptions 
{ 
    Protocol = Protocol.Ftp, 
    HostName = "example.com", 
    UserName = "user", 
    Password = "password", 
}; 

using (Session session = new Session()) 
{ 
    // Connect 
    session.Open(sessionOptions); 

    List<string> prevFiles = null; 

    while (true) 
    { 
     // Collect file list 
     List<string> files = 
      session.EnumerateRemoteFiles(
       "/remote/path", "*.*", EnumerationOptions.AllDirectories) 
      .Select(fileInfo => fileInfo.FullName) 
      .ToList(); 
     if (prevFiles == null) 
     { 
      // In the first round, just print number of files found 
      Console.WriteLine("Found {0} files", files.Count); 
     } 
     else 
     { 
      // Then look for differences against the previous list 
      IEnumerable<string> added = files.Except(prevFiles); 
      if (added.Any()) 
      { 
       Console.WriteLine("Added files:"); 
       foreach (string path in added) 
       { 
        Console.WriteLine(path); 
       } 
      } 

      IEnumerable<string> removed = prevFiles.Except(files); 
      if (removed.Any()) 
      { 
       Console.WriteLine("Removed files:"); 
       foreach (string path in removed) 
       { 
        Console.WriteLine(path); 
       } 
      } 
     } 

     prevFiles = files; 

     Console.WriteLine("Sleeping 10s..."); 
     Thread.Sleep(10000); 
    } 
} 

(yo soy el autor de WinSCP)


embargo, si usted quiere realmente sólo tiene que descargar los cambios, que es una manera más fácil . Simplemente use el Session.SynchronizeDirectories en el ciclo.

session.SynchronizeDirectories(
    SynchronizationMode.Local, "/remote/path", @"C:\local\path", true).Check(); 

Si no desea utilizar una biblioteca tercera parte, que tienen que ver con las limitaciones de la FtpWebRequest. Para ver un ejemplo de cómo enumerar recursivamente un árbol de directorio remoto con el FtpWebRequest, consulte mi respuesta al C# Download all files and subdirectories through FTP.