2 Stimmen

Das Herunterladen aller Dateien mit FTP und C#

Was ist der beste Weg, um alle Dateien in einem Remote-Verzeichnis mit C# und FTP herunterzuladen und in einem lokalen Verzeichnis zu speichern?

Vielen Dank.

3voto

Martin Vobr Punkte 5673

Das Herunterladen aller Dateien in einem bestimmten Ordner scheint eine einfache Aufgabe zu sein. Es gibt jedoch einige Probleme, die gelöst werden müssen. Um nur einige zu nennen:

  • Wie bekomme ich eine Liste der Dateien (System.Net.FtpWebRequest liefert Ihnen eine nicht analysierte Liste und das Verzeichnislistformat ist in keinem RFC standardisiert)?
  • Was ist, wenn das Remote-Verzeichnis sowohl Dateien als auch Unterverzeichnisse enthält? Müssen wir in die Unterverzeichnisse eintauchen und deren Inhalt herunterladen?
  • Was ist, wenn einige der Remote-Dateien bereits auf dem lokalen Computer vorhanden sind? Sollen sie überschrieben werden? Übersprungen? Sollten nur ältere Dateien überschrieben werden?
  • Was ist, wenn die lokale Datei nicht beschreibbar ist? Soll der gesamte Transfer fehlschlagen? Sollen wir die Datei überspringen und mit der nächsten fortfahren?
  • Wie sollen Dateien auf einer entfernten Festplatte behandelt werden, die aufgrund unzureichender Zugriffsrechte nicht lesbar sind?
  • Wie werden die Symbolischen Links, Hard Links und Junction Points behandelt? Links können leicht verwendet werden, um eine unendliche rekursive Verzeichnisbaumstruktur zu erstellen. Betrachten Sie Ordner A mit dem Unterordner B, der in Wirklichkeit nicht der eigentliche Ordner ist, sondern der *nix Hard Link, der zurück auf Ordner A zeigt. Der naive Ansatz führt zu einer Anwendung, die niemals endet (zumindest solange niemand den Stecker zieht).

Anständige FTP-Komponenten von Drittanbietern sollten eine Methode zur Behandlung dieser Probleme haben. Der folgende Code verwendet unser Rebex FTP für .NET.

using (Ftp client = new Ftp())
        {
            // Verbindung zum FTP-Server herstellen und anmelden
            client.Connect("mirror.aarnet.edu.au");
            client.Login("anonymous", "my@password");

            // alle Dateien herunterladen
            client.GetFiles(
                "/pub/fedora/linux/development/i386/os/EFI/*",
                "c:\\temp\\download",
                FtpBatchTransferOptions.Recursive,
                FtpActionOnExistingFiles.OverwriteAll
            );

            client.Disconnect();
        }

Der Code stammt aus meinem Blogbeitrag, der auf blog.rebex.net verfügbar ist. Der Blogbeitrag verweist auch auf ein Beispiel, in dem gezeigt wird, wie der Benutzer gefragt wird, wie jedes Problem behandelt werden soll (z. B. Überschreiben/Ältere überschreiben/Überspringen/Alle überspringen).

3voto

Martin Prikryl Punkte 164336

Für das FTP-Protokoll können Sie die FtpWebRequest-Klasse aus dem .NET-Framework verwenden. Es hat jedoch keine explizite Unterstützung für rekursive Dateioperationen (einschließlich Downloads). Sie müssen die Rekursion selbst implementieren:

  • Liste das Remote-Verzeichnis auf
  • Iteriere über die Einträge, lade Dateien herunter und gehe in Unterverzeichnisse (liste sie erneut usw.)

Der schwierige Teil besteht darin, Dateien aus Unterverzeichnissen zu identifizieren. Mit dem FtpWebRequest gibt es keine Möglichkeit, dies auf eine tragbare Weise zu tun. Der FtpWebRequest unterstützt leider nicht das MLSD-Befehl, der der einzige tragbare Weg ist, ein Verzeichnislisting mit Dateiattributen im FTP-Protokoll abzurufen. Siehe auch Überprüfen, ob Objekt auf FTP-Server Datei oder Verzeichnis ist.

Ihre Möglichkeiten sind:

  • Führen Sie eine Operation mit einem Dateinamen durch, die sicher für Dateien fehlschlägt und für Verzeichnisse erfolgreich ist (oder umgekehrt). Sie können z.B. versuchen, den "Namen" herunterzuladen. Wenn das gelingt, handelt es sich um eine Datei, wenn das fehlschlägt, handelt es sich um ein Verzeichnis. Aber das kann ein Leistungsproblem darstellen, wenn Sie eine große Anzahl von Einträgen haben.
  • Sie können Glück haben und in Ihrem speziellen Fall anhand eines Dateinamens eine Datei von einem Verzeichnis unterscheiden (d.h. alle Ihre Dateien haben eine Erweiterung, während Unterverzeichnisse dies nicht tun)
  • Sie verwenden ein langes Verzeichnislisting (LIST-Befehl = ListDirectoryDetails-Methode) und versuchen, ein server-spezifisches Listing zu analysieren. Viele FTP-Server verwenden ein *nix-Format beim Listing, bei dem Sie ein Verzeichnis am Anfang des Eintrags am Buchstaben d erkennen. Aber viele Server verwenden ein anderes Format. Das folgende Beispiel verwendet diesen Ansatz (unter der Annahme des *nix-Formats)

    void DownloadFtpDirectory( string url, NetworkCredential credentials, string localPath) { FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url); listRequest.UsePassive = true; listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; listRequest.Credentials = credentials;

    List lines = new List();
    
    using (WebResponse listResponse = 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')
        {
            Directory.CreateDirectory(localFilePath);
            DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath);
        }
        else
        {
            var downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl);
            downloadRequest.UsePassive = true;
            downloadRequest.UseBinary = true;
            downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile;
            downloadRequest.Credentials = credentials;
            var response = downloadRequest.GetResponse();
    
            using (Stream ftpStream = response.GetResponseStream())
            using (Stream fileStream = File.Create(localFilePath))
            {
                ftpStream.CopyTo(fileStream);
            }
        }
    }

    }

Die url muss wie folgt sein:

  • ftp://example.com/ oder
  • ftp://example.com/path/

Oder verwenden Sie eine 3rd-Party-Bibliothek, die rekursive Downloads unterstützt.

Zum Beispiel können Sie mit der WinSCP .NET Assembly das gesamte Verzeichnis mit einem einzigen Aufruf von Session.GetFiles herunterladen:

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

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

    // Download files
    session.GetFiles("/home/user/*", @"d:\download\").Check();
} 

Intern verwendet WinSCP den MLSD-Befehl, sofern vom Server unterstützt. Andernfalls verwendet es den LIST-Befehl und unterstützt Dutzende verschiedener Listing-Formate.

(Ich bin der Autor von WinSCP)

1 Stimmen

Gerade dabei, WinSCP für genau diesen Zweck herunterzuladen, danke im Voraus. Habe das Gefühl, dass mir das etwas Zeit sparen könnte.

2voto

Handprint Punkte 434

Mit C# FtpWebRequest und FtpWebReponse können Sie die folgende Rekursion verwenden (stellen Sie sicher, dass die Ordnerzeichenfolgen mit '\' enden):

    public void GetAllDirectoriesAndFiles(string getFolder, string putFolder)
    {
        List dirIitems = DirectoryListing(getFolder);
        foreach (var item in dirIitems)
        {
            if ( item.Contains('.')  )
            {
                GetFile(getFolder + item, putFolder + item);
            }
            else
            {
                var subDirPut = new DirectoryInfo(putFolder + "\\" + item);
                subDirPut.Create();
                GetAllDirectoriesAndFiles(getFolder + item + "\\", subDirPut.FullName + "\\");
            }
        }
    }

Das "item.Contains('.')" ist etwas primitiv, hat aber für meine Zwecke funktioniert. Hinterlassen Sie einen Kommentar, wenn Sie ein Beispiel für die Methoden benötigen:

GetFile(string getFileAndPath, string putFileAndPath)

oder

DirectoryListing(getFolder)

1voto

Jérémie Bertrand Punkte 2987

Sie können FTPClient von laedit.net verwenden. Es steht unter der Apache-Lizenz und ist einfach zu bedienen.

Es verwendet FtpWebRequest :

  • Zuerst müssen Sie WebRequestMethods.Ftp.ListDirectoryDetails verwenden, um die Details aller Listen im Ordner zu erhalten
  • Für jede Datei müssen Sie WebRequestMethods.Ftp.DownloadFile verwenden, um sie in einen lokalen Ordner herunterzuladen

1voto

STW Punkte 42452

Sie könnten System.Net.WebClient.DownloadFile() verwenden, das FTP unterstützt. MSDN Details hier

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X