C # Télécharger tous les fichiers et sous-répertoires via FTP

Informations générales
Je suis encore en train d’apprendre le C #. Pour m’aider, j’essaie de créer un programme qui synchronise automatiquement tous mes projets locaux avec un dossier sur mon serveur FTP. Ceci afin que, que je sois à l’école ou à la maison, j’ai toujours les mêmes projets à ma disposition.

Je sais qu’il existe déjà des programmes comme Dropbox qui le font déjà pour moi, mais je me suis dit que créer quelque chose comme ça m’apprendrait beaucoup en cours de route.

Le problème
Mon premier pas vers mon objective était de télécharger tous les fichiers, sous-répertoires et sous-fichiers à partir de mon serveur FTP. J’ai réussi à télécharger tous les fichiers d’un répertoire avec le code ci-dessous. Cependant, mon code ne répertorie que les noms de dossiers et les fichiers du répertoire principal. Les sous-dossiers et les sous-fichiers ne sont jamais retournés ni téléchargés. En plus de cela, le serveur renvoie une erreur 550 car j’essaie de télécharger les dossiers comme s’il s’agissait de fichiers. Cela fait plus de 4 heures que je suis sur ce sujet, mais je ne trouve rien sur la façon de résoudre ces problèmes et de le faire fonctionner. J’espère donc que vous allez m’aider 🙂

Code

public ssortingng[] GetFileList() { ssortingng[] downloadFiles; SsortingngBuilder result = new SsortingngBuilder(); WebResponse response = null; StreamReader reader = null; try { FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url); request.UseBinary = true; request.Method = WebRequestMethods.Ftp.ListDirectory; request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord); request.KeepAlive = false; request.UsePassive = false; response = request.GetResponse(); reader = new StreamReader(response.GetResponseStream()); ssortingng line = reader.ReadLine(); while (line != null) { result.Append(line); result.Append("\n"); line = reader.ReadLine(); } result.Remove(result.ToSsortingng().LastIndexOf('\n'), 1); return result.ToSsortingng().Split('\n'); } catch (Exception ex) { if (reader != null) { reader.Close(); } if (response != null) { response.Close(); } downloadFiles = null; return downloadFiles; } } private void Download(ssortingng file) { try { ssortingng uri = url + "/" + file; Uri serverUri = new Uri(uri); if (serverUri.Scheme != Uri.UriSchemeFtp) { return; } FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file); request.UseBinary = true; request.Method = WebRequestMethods.Ftp.DownloadFile; request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord); request.KeepAlive = false; request.UsePassive = false; FtpWebResponse response = (FtpWebResponse)request.GetResponse(); Stream responseStream = response.GetResponseStream(); FileStream writeStream = new FileStream(localDestnDir + "\\" + file, FileMode.Create); int Length = 2048; Byte[] buffer = new Byte[Length]; int bytesRead = responseStream.Read(buffer, 0, Length); while (bytesRead > 0) { writeStream.Write(buffer, 0, bytesRead); bytesRead = responseStream.Read(buffer, 0, Length); } writeStream.Close(); response.Close(); } catch (WebException wEx) { MessageBox.Show(wEx.Message, "Download Error"); } catch (Exception ex) { MessageBox.Show(ex.Message, "Download Error"); } } 

FtpWebRequest ne prend pas en charge explicitement les opérations de fichier récursives (y compris les téléchargements). Vous devez implémenter la récursion vous-même:

  • Lister le répertoire distant
  • Itérer les entrées, télécharger des fichiers et les faire rentrer dans des sous-répertoires (en les listant à nouveau, etc.)

La partie délicate consiste à identifier les fichiers de sous-répertoires. Il n’y a aucun moyen de faire cela de manière portable avec FtpWebRequest . Malheureusement, FtpWebRequest ne prend pas en charge la commande MLSD , qui est le seul moyen portable de récupérer une liste de répertoires avec des atsortingbuts de fichier dans le protocole FTP. Voir aussi Vérification si l’object sur le serveur FTP est un fichier ou un répertoire .

Vos options sont:

  • Effectuez une opération sur un nom de fichier qui est voué à l’échec pour le fichier et qui réussit aux répertoires (ou inversement). C’est-à-dire que vous pouvez essayer de télécharger le “nom”. Si cela réussit, c’est un fichier, si cela échoue, c’est un répertoire.
  • Vous pouvez avoir de la chance et dans votre cas particulier, vous pouvez distinguer un fichier d’un répertoire par un nom de fichier (c’est-à-dire que tous vos fichiers ont une extension, contrairement aux sous-répertoires).
  • Vous utilisez une longue liste de répertoires (commande LIST = méthode ListDirectoryDetails ) et essayez d’parsingr une liste spécifique au serveur. De nombreux serveurs FTP utilisent une liste de style * nix, dans laquelle vous identifiez un répertoire par le d au tout début de l’entrée. Mais de nombreux serveurs utilisent un format différent. L’exemple suivant utilise cette approche (en supposant le format * nix)
 void DownloadFtpDirectory(ssortingng url, NetworkCredential credentials, ssortingng localPath) { FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url); listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; listRequest.Credentials = credentials; List lines = new List(); using (FtpWebResponse listResponse = (FtpWebResponse)listRequest.GetResponse()) using (Stream listStream = listResponse.GetResponseStream()) using (StreamReader listReader = new StreamReader(listStream)) { while (!listReader.EndOfStream) { lines.Add(listReader.ReadLine()); } } foreach (ssortingng line in lines) { ssortingng[] tokens = line.Split(new[] { ' ' }, 9, SsortingngSplitOptions.RemoveEmptyEnsortinges); ssortingng name = tokens[8]; ssortingng permissions = tokens[0]; ssortingng localFilePath = Path.Combine(localPath, name); ssortingng fileUrl = url + name; if (permissions[0] == 'd') { if (!Directory.Exists(localFilePath)) { Directory.CreateDirectory(localFilePath); } DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath); } else { FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl); downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile; downloadRequest.Credentials = credentials; using (FtpWebResponse downloadResponse = (FtpWebResponse)downloadRequest.GetResponse()) using (Stream sourceStream = downloadResponse.GetResponseStream()) using (Stream targetStream = File.Create(localFilePath)) { byte[] buffer = new byte[10240]; int read; while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0) { targetStream.Write(buffer, 0, read); } } } } } 

Utilisez la fonction comme:

 NetworkCredential credentials = new NetworkCredential("user", "mypassword"); ssortingng url = "ftp://ftp.example.com/directory/to/download/"; DownloadFtpDirectory(url, credentials, @"C:\target\directory"); 

Si vous souhaitez éviter les problèmes d’parsing des formats de liste de répertoires spécifiques au serveur, utilisez une bibliothèque tierce qui prend en charge la commande MLSD et / ou en analysant divers formats de liste LIST ; et téléchargements récursifs.

Par exemple, avec l’ assemblage WinSCP .NET, vous pouvez télécharger le répertoire entier en un seul appel à Session.GetFiles :

 // Setup session options SessionOptions sessionOptions = new SessionOptions { Protocol = Protocol.Ftp, HostName = "ftp.example.com", UserName = "user", Password = "mypassword", }; using (Session session = new Session()) { // Connect session.Open(sessionOptions); // Download files session.GetFiles("/directory/to/download/*", @"C:\target\directory\*").Check(); } 

En interne, WinSCP utilise la commande MLSD , si elle est prise en charge par le serveur. Sinon, il utilise la commande LIST et prend en charge des dizaines de formats de liste différents.

La méthode Session.GetFiles est récursive par défaut.

(Je suis l’auteur de WinSCP)