C # télécharger tous les fichiers et sous-répertoires par FTP

Infos Générales

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

je sais qu'il y a des programmes comme Dropbox qui font déjà ça pour moi, mais je me suis dit que créer quelque chose comme ça moi-même, j'en apprendrai beaucoup en cours de route.

le problème

Ma première étape vers mon objectif était de simplement 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 Liste que les noms de dossiers et les fichiers du répertoire principal. Les sous-dossiers et sous-fichiers ne sont jamais retournés et jamais téléchargé. En dehors de cela, le serveur retourne une erreur 550 parce que j'essaie de télécharger les dossiers comme s'ils étaient des fichiers. Je suis dessus depuis plus de 4 heures maintenant, mais je ne trouve rien sur la façon de résoudre ces problèmes et de le faire fonctionner. Par conséquent, j'espère que vous allez m'aider:)

Code

public string[] GetFileList()
{
    string[] downloadFiles;
    StringBuilder result = new StringBuilder();
    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());
        string line = reader.ReadLine();
        while (line != null)
        {
            result.Append(line);
            result.Append("n");
            line = reader.ReadLine();
        }
        result.Remove(result.ToString().LastIndexOf('n'), 1);
        return result.ToString().Split('n');
    }
    catch (Exception ex)
    {
        if (reader != null)
        {
            reader.Close();
        }
        if (response != null)
        {
            response.Close();
        }
        downloadFiles = null;
        return downloadFiles;
    }
}

private void Download(string file)
{
    try
    {
        string 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");
    }
}
13
demandé sur Martin Prikryl 2016-05-05 00:50:01

1 réponses

le FtpWebRequest ne supporte pas explicitement les opérations de fichiers récursifs (y compris les téléchargements). Vous devez mettre en œuvre vous-même la récursion:

  • Liste du répertoire distant
  • Itérer les entrées, le téléchargement de fichiers et la récurrence dans les sous-répertoires (les énumérant à nouveau, etc.)

partie délicate est d'identifier les fichiers à partir de sous-répertoires. Il n'y a aucun moyen de faire cela dans un mobiles avec le FtpWebRequest . Le FtpWebRequest ne supporte malheureusement pas la commande MLSD , qui est la seule façon portable de récupérer la liste des répertoires avec les attributs des fichiers dans le protocole FTP. Voir aussi vérifier si l'objet sur le serveur FTP est FICHIER ou répertoire .

vos options sont:

  • faire une opération sur un nom de fichier qui est certain d'échouer pour le fichier et succède pour les répertoires (ou vice inversement.) I. e. 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 être chanceux et dans votre cas spécifique, vous pouvez dire un fichier à partir d'un répertoire par un nom de fichier (i.e. tous vos fichiers ont une extension, tandis que les sous-répertoires ne le font pas)
  • vous utilisez une longue liste de répertoires ( LIST commande = ListDirectoryDetails méthode) et essayez d'analyser une liste propre au serveur. De nombreux serveurs FTP utilisent le style * nix listing, où 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(string url, NetworkCredential credentials, string localPath)
{
    FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url);
    listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
    listRequest.Credentials = credentials;

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

    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 (string line in lines)
    {
        string[] tokens =
            line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
        string name = tokens[8];
        string permissions = tokens[0];

        string localFilePath = Path.Combine(localPath, name);
        string 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);
                }
            }
        }
    }
}

utiliser la fonction comme:

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

MLSD commande et / ou Analyse de divers formats de listage LIST ; et téléchargements récursifs.

par exemple avec WinSCP. net assembly vous pouvez télécharger le répertoire entier avec un seul appel à la 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. Si non, il utilise la commande LIST et supporte des douzaines de formats de listing.

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

(je suis l'auteur de WinSCP)

24
répondu Martin Prikryl 2017-11-23 07:48:10