[C#] Pobieranie listy katalogów z serwera FTP

Witam, napisałem następujący program:

public partial class Form1 : Form

    {


        public Form1()

        {

            InitializeComponent();

            PobierzDyski();

            textBox1.Text = "ftp://";

        }


        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)

        {


        }


        public void PobierzDyski()

        {

            string[] strDrives = Environment.GetLogicalDrives();

            foreach (string name in strDrives)

            {

                try

                {

                    DirectoryInfo dir = new DirectoryInfo(name);

                    dir.GetDirectories();

                    TreeNode ndRoot = new TreeNode(name);

                    treeView1.Nodes.Add(ndRoot);

                    PobierzListePlikow(ndRoot,ndRoot.Text);

                }

                catch

                {

                }

            }


            Application.DoEvents();


        }


        private void PobierzListePlikow(TreeNode parentNode, string fullName)

        {


            DirectoryInfo dir = new DirectoryInfo(fullName);

            DirectoryInfo[] dirSubs = dir.GetDirectories();



            foreach (DirectoryInfo dirSub in dirSubs)

            {

                if ((dirSub.Attributes & FileAttributes.Hidden) != 0)

                {

                    continue;

                }


                TreeNode subNode = new TreeNode(dirSub.Name);

                parentNode.Nodes.Add(subNode);

                DirectoryInfo dir2 = new DirectoryInfo(dirSub.FullName);





                if (dir2 != null)

                {

                    PobierzListePlikow(subNode, dirSub.FullName);

                }


            }


            FileInfo[] files = dir.GetFiles();


            foreach (FileInfo file in files)

            {

                TreeNode fileNode = new TreeNode(file.ToString());

                parentNode.Nodes.Add(fileNode);

            }


        }


        private void button1_Click(object sender, EventArgs e)

        {

            string uri = textBox1.Text;

            FtpWebRequest klient = (FtpWebRequest)FtpWebRequest.Create(uri) ;


            klient.Credentials = new NetworkCredential(textBox2.Text, textBox3.Text);

            klient.Method = WebRequestMethods.Ftp.ListDirectory;


            FtpWebResponse odp = (FtpWebResponse)klient.GetResponse();

            Stream responseStream = odp.GetResponseStream();

            StreamReader reader = new StreamReader(responseStream);



        }


    }

W programie utworzyłem treeView1 i treeView2. W 1 jest drzewo katalogów i plików na komputerze, natomiast w 2 chciałem, by było drzewo katalogów i plików na serwerze FTP (button1_click). I właśnie w tym momencie utknąłem. Czy jest możliwość, żeby stworzyć to drzewo dla serwera FTP analogicznie jak na komputerze? Czy można użyć do tego DirectoryInfo? Jeżeli nie, to jaki jest inny sposób?

Nie, DirectoryInfo nie obsługuje protokołu FTP(obsługuje coś innego niż lokalne dyski? SMB?). Do obsługi tego możesz wykorzystać FtpWebRequest z metodą “NLIST”(WebRequestMethods.Ftp.ListDirectory), sparsować odpowiedź i, podobnie jak lokalnie, zrobić to rekurencyjnie dla każdego katalogu.

Kod prezentuje się następująco:

public partial class Form1 : Form

    {


        public Form1()

        {

            InitializeComponent();

            PobierzDyski();

            textBox1.Text = "ftp://";

        }


        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)

        {


        }


        public void PobierzDyski()

        {

            string[] strDrives = Environment.GetLogicalDrives();

            foreach (string name in strDrives)

            {

                try

                {

                    DirectoryInfo dir = new DirectoryInfo(name);

                    dir.GetDirectories();

                    TreeNode ndRoot = new TreeNode(name);

                    treeView1.Nodes.Add(ndRoot);

                    PobierzListePlikow(ndRoot,ndRoot.Text);

                }

                catch

                {

                }

            }


            Application.DoEvents();


        }


        private void PobierzListePlikow(TreeNode parentNode, string fullName)

        {


            DirectoryInfo dir = new DirectoryInfo(fullName);

            DirectoryInfo[] dirSubs = dir.GetDirectories();


           // ImageList imageList1 = new ImageList();

           // imageList1.Images.Add(Image.FromFile("c:\\Folder.bmp"));

         // treeView1.ImageList = imageList1;


            foreach (DirectoryInfo dirSub in dirSubs)

            {

                if ((dirSub.Attributes & FileAttributes.Hidden) != 0)

                {

                    continue;

                }


                TreeNode subNode = new TreeNode(dirSub.Name);

                parentNode.Nodes.Add(subNode);

                DirectoryInfo dir2 = new DirectoryInfo(dirSub.FullName);



             // parentNode.ImageIndex = 0;


                if (dir2 != null)

                {

                    PobierzListePlikow(subNode, dirSub.FullName);

                }


            }


            FileInfo[] files = dir.GetFiles();


            foreach (FileInfo file in files)

            {

                TreeNode fileNode = new TreeNode(file.ToString());

                parentNode.Nodes.Add(fileNode);

            }


        }


        private void button1_Click(object sender, EventArgs e)

        {

            string uri = textBox1.Text;

            string katalog;

            string[] foldery;

            FtpWebRequest klient = (FtpWebRequest)FtpWebRequest.Create(uri) ;


            klient.Credentials = new NetworkCredential(textBox2.Text, textBox3.Text);

            klient.Method = WebRequestMethods.Ftp.ListDirectory;


            FtpWebResponse odp = (FtpWebResponse)klient.GetResponse();

            Stream responseStream = odp.GetResponseStream();

            StreamReader reader = new StreamReader(responseStream);

            katalog = reader.ReadToEnd();

            foldery = katalog.Split('\n');


            foreach (string directory in foldery)

            {

                if (directory != "")

                {

                    TreeNode dirNode = new TreeNode(directory);

                    treeView2.Nodes.Add(dirNode);

                     PobierzListePlikowFTP(dirNode, dirNode.Text.Substring(0,dirNode.Text.Length-1), uri);

                }

            }

        }

        public void PobierzListePlikowFTP(TreeNode parentNode, string name, string uri)

        {

            bool flaga;

            string temp;

            string katalog;

            string[] foldery;


            FtpWebRequest klient = (FtpWebRequest)FtpWebRequest.Create(uri +"/"+ name);


            uri = uri + "/" + name;


            klient.Credentials = new NetworkCredential(textBox2.Text, textBox3.Text);


            klient.Method = WebRequestMethods.Ftp.ListDirectory;


            FtpWebResponse odp = (FtpWebResponse)klient.GetResponse();

            Stream responseStream = odp.GetResponseStream();

            StreamReader reader = new StreamReader(responseStream);

            katalog = reader.ReadToEnd();

            foldery = katalog.Split('\n');


            foreach (string directory in foldery)

            {

                TreeNode dirNode = new TreeNode(directory);

                // jeśli folder nie jest pusty

                if (directory != "")

                {

                    flaga = true;

                    try

                    {

                        temp = dirNode.Text.Substring(name.Length + 1, directory.Length - (name.Length + 2));

                        dirNode.Text = temp;

                        parentNode.Nodes.Add(dirNode);

                        for (int i = 0; i < temp.Length; i++)

                        {

                            // jeśli jest plikiem

                            if (temp[i] == '.')

                            {

                                flaga = false;

                            }

                        }

                        // jeśli jest folderem to pobierz rekrurencyjnie jego zawartość

                        if (flaga == true)

                        {

                            PobierzListePlikowFTP(dirNode, temp, uri);

                        }

                    }

                    catch

                    {

                        temp = dirNode.Text.Substring(0, dirNode.Text.Length - 1);

                        parentNode.Nodes.Add(temp);

                    }

                }

                else

                {

                    parentNode.Nodes.Add(dirNode.Text);

                }

                label3.Text = dirNode.Text;



            }

        }



    }

Teraz już program pobiera pliki i katalogi tylko jest jeden problem. Kiedy kliknę na przycisk, żeby połączył się z serwerem i pobrał zawartość katalogów, to zajmuje mu to ponad 10 minut. Jest jakiś sposób, który pozwoliłby zwiększyć szybkość działania programu? Bo jak widzę, to rekurencja to chyba nie jest najlepszy pomysł

Niestety, są to uroki korzystania z sieci i protokołu FTP(+ wbudowanych klas w .NET, które są dość wolne, ale dlaczego tak jest nie jestem pewien, pewnie chodzi o nieutrzymywanie połączenia z serwerem). Usprawnienie jakie widzę, to doczytywać zawartość poszczególnych folderów przy rozwijaniu gałęzi drzewa albo ręcznie zaimplementować obsługę FTP wykorzystując Socket/TcpClient i wiele wątków, do pobierania zawartości podfolderów równolegle(co nie będzie to bardzo trudne, ale niekoniecznie da oczekiwane rezultaty). Można też poszukać jakiegoś istniejącego rozwiązania różnego od FtpWebRequest

O ile dobrze widzę, to tutaj rekurencyjnie pobierane jest całe drzewo plików i katalogów z FTP. Nie widziałem jeszcze żadnego klienta FTP, który tak by działał - pewno nie bez przyczyny, taka operacja nigdy nie ma sensu. To tak, jakby pisać przeglądarkę internetową, która przy uruchamianiu ściąga do swojego cache cały internet.

Tylko bez teorii spiskowych. :stuck_out_tongue:

Trochę pospiskować też należy :stuck_out_tongue: Akurat klasy *WebRequest prędkością(inicjalizacji szczególnie) nie grzeszą, chyba że w 4.0 coś się w porównaniu z 3.5 zmieniło.

Nie sądzę, aby program napisany w innej technologii był znacząco szybszy. Tu jest błąd w koncepcji.

Ale je nie mówię, że to jest dobrze napisane. Ja mówię ogólnie o *WebRequest, które działają… stosunkowo wolno.

@Fiołek

Faktycznie, zaimplementowanie obsługi protokołu FTP w oparciu o TcpClient oraz TcpListener (samo TcpClient nie wystarczy) mogłoby tutaj być najwydajniejszym rozwiązaniem, bo można by skorzystać z polecenia CWD protokołu FTP. Ale ja bym się tak od razu na to nie porywał, a spróbował najpierw poprawić ten kod.

@eureka 170

Mógłbyś mi pokazać co otrzymasz jak wykonasz taki kod dla głównego katalogu na serwerze ftp i takiego w którym jest jakiś plik (oczywiście adres URL oraz nazwę użytkownika i hasło musisz sobie ustawić):

FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://127.0.0.1/");

request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

request.Credentials = new NetworkCredential("", "");


FtpWebResponse response = (FtpWebResponse)request.GetResponse();

Stream responseStream = response.GetResponseStream();

StreamReader reader = new StreamReader(responseStream);


richTextBox1.Text += reader.ReadToEnd();


reader.Close();

response.Close();

Dodane 09.07.2011 (So) 16:23 – Tu masz trochę uporządkowaną wersję kodu … Zobacz, czy to będzie działać wolniej czy szybciej FTPClient.cs

using System;

using System.IO;

using System.Net;


namespace FTPDemo

{

    public class FTPClient

    {

        public FTPClient(string userName, string password)

        {

            UserName = string.IsNullOrEmpty(userName) ? string.Empty : userName;

            Password = string.IsNullOrEmpty(password) ? string.Empty : password;

            KeepAlive = true;

            UsePassive = true;

            EnableSsl = false;

            Timeout = System.Threading.Timeout.Infinite;

        }


        public string UserName { get; set; }

        public string Password { get; set; }

        public bool KeepAlive { get; set; }

        public bool UsePassive { get; set; }

        public bool EnableSsl { get; set; }

        public int Timeout { get; set; }


        public FTPObject[] GetDirectoryList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectory);

                return list != null ? ParseDirectoryList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public FTPObject[] GetDirectoryDetailsList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectoryDetails);

                return list != null ? ParseDirectoryDetailsList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public string CombineUrl(string url1, string url2)

        {

            if (string.IsNullOrEmpty(url1))

                return url2;

            if (string.IsNullOrEmpty(url2))

                return url1;


            url1 = url1.TrimEnd(new char[] { '/' });

            url2 = url2.TrimStart(new char[] { '/' });


            return string.Format("{0}/{1}", url1, url2);

        }


        private FtpWebRequest GetRequest(string url)

        {

            try

            {

                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);

                request.Credentials = new NetworkCredential(UserName, Password);

                request.KeepAlive = KeepAlive;

                request.UsePassive = UsePassive;

                request.EnableSsl = EnableSsl;

                request.Timeout = Timeout;

                return request;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        private string[] GetList(string url, string method)

        {

            FtpWebResponse response = null;

            Stream responseStream = null;

            StreamReader reader = null;


            try

            {

                FtpWebRequest request = GetRequest(url);

                request.Method = method;


                response = (FtpWebResponse)request.GetResponse();

                responseStream = response.GetResponseStream();

                reader = new StreamReader(responseStream);


                string result = reader.ReadToEnd();

                return !string.IsNullOrEmpty(result) ? result.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

            finally

            {

                if (reader != null)

                    reader.Close();

                if (responseStream != null)

                    responseStream.Close();

                if (response != null)

                    response.Close();

            }

        }


        private FTPObject[] ParseDirectoryList(string url, string[] list)

        {

            FTPObject[] result = new FTPObject[list.Length];


            for (int i = 0; i < list.Length; i++)

            {

                bool isDirectory = true;

                if (!string.IsNullOrEmpty(Path.GetExtension(list[i])))

                    isDirectory = false;


                FTPObject ftpObject = new FTPObject(url, list[i], isDirectory);

                result[i] = ftpObject;

            }


            return result;

        }


        private FTPObject[] ParseDirectoryDetailsList(string url, string[] list)

        {

            return null;

        }

    }

}

FTPObject.cs

using System;


namespace FTPDemo

{

    public class FTPObject

    {

        public FTPObject(string url, string name, bool isDirectory)

        {

            Name = name;

            Url = url;

            IsDirectory = isDirectory;

        }


        public string Url { get; private set; }

        public string Name { get; private set; }

        public bool IsDirectory { get; private set; }

    }

}

Form1.cs

using System;

using System.Windows.Forms;


namespace FTPDemo

{

    public partial class Form1 : Form

    {

        FTPClient client = null;


        public Form1()

        {

            InitializeComponent();

        }


        private void Form1_Load(object sender, EventArgs e)

        {

            client = new FTPClient(ftpUserNameTextBox.Text, ftpPasswordTextBox.Text);

        }


        private void refreshButton_Click(object sender, EventArgs e)

        {

            Fill(ftpTreeView, ftpUrlTextBox.Text);

        }


        private void Fill(TreeView tree, string url)

        {

            tree.Nodes.Clear();


            FTPObject[] list = client.GetDirectoryList(url);

            if (list != null)

            {

                foreach (FTPObject o in list)

                {

                    TreeNode node = new TreeNode(o.Name);

                    tree.Nodes.Add(node);


                    if (o.IsDirectory)

                        Fill(node, client.CombineUrl(url, o.Name));

                }

            }

        }


        private void Fill(TreeNode parentNode, string url)

        {

            FTPObject[] list = client.GetDirectoryList(url);

            if (list != null)

            {

                foreach (FTPObject o in list)

                {

                    TreeNode node = new TreeNode(o.Name);

                    parentNode.Nodes.Add(node);


                    if (o.IsDirectory)

                        Fill(node, client.CombineUrl(url, o.Name));

                }

            }

        }

    }

}

[bZmieniłem]Form1.cs

namespace WindowsFormsApplication5

{

    public partial class Form1 : Form

    {


        FTPClient client = null;


        public Form1()

        {

            InitializeComponent();

        }


        private void Form1_Load(object sender, EventArgs e)

        {

            textBox1.Text = "ftp://";


        }


        private void Fill(TreeView tree, string url)

        {

            tree.Nodes.Clear();


            FTPObject[] list = client.GetDirectoryList(url);

            if (list != null)

            {

                foreach (FTPObject o in list)

                {

                    TreeNode node = new TreeNode(o.Name);

                    tree.Nodes.Add(node);


                    if (o.IsDirectory)

                        Fill(node, client.CombineUrl(url, o.Name));

                }

            }

        }


        private void Fill(TreeNode parentNode, string url)

        {

            FTPObject[] list = client.GetDirectoryList(url);

            if (list != null)

            {

                foreach (FTPObject o in list)

                {

                    TreeNode node = new TreeNode(o.Name);

                    parentNode.Nodes.Add(node);


                    if (o.IsDirectory)

                        Fill(node, client.CombineUrl(url, o.Name));

                }

            }

        }


        private void button1_Click(object sender, EventArgs e)

        {

            client = new FTPClient(textBox2.Text, textBox3.Text);

            Fill(treeView2, textBox1.Text);

        }

    }

}

Przeniosłem tutaj client = new FTPClient(textBox2.Text, textBox3.Text); funkcji button1_Click. Reszta jest bez zmian.

Wyskakuje mi wyjątek: “Serwer zdalny zwrcił błąd: (550) Plik niedostępny (np. nie znaleziono pliku, brak dostępu).”

W sumie to nie wiem, dlaczego wyrzuca Ci taki wyjątek (ja ten kod testowałem na lokalnym serwerze ftp i niby działa). Jak zalogujesz się do tego serwera ftp, z którego korzystasz, np. za pomocą wiersza poleceń (narzędzie ftp) lub windows explorer-a to możesz normalnie przeglądać katalogi? Możesz jeszcze puścić ten kod przez debugger, znaleźć moment, w którym wyrzucany jest wyjątek i przyjrzeć się ścieżce folderu, który prawdopodobnie jest wtedy otwierany. Sprawdź, czy ta ścieżka na pewno jest poprawna.

No i nie napisałeś mi jaki w Twoim przypadku jest rezultat użycia metody ListDirectoryDetails (prosiłem o to wyżej). Nazw plików i katalogów nie musisz mi pokazywać (możesz je np. zakolorować). Wystarczy mi kilka pozycji.

Tutaj jest wynik działania metody ListDirectoryDetails http://imageshack.us/photo/my-images/9/ … d01co.png/

A tutaj jest ten błąd: http://imageshack.us/photo/my-images/69 … 01qca.jpg/

W tym błędzie jak widać, program źle odczytuje adres bo w tym przypadku wchodzi do ftp.helion.pl/pub/pub, a w folderze pub nie ma folderu “pub”. Podobnie jest na innym serwerze, program wysypuje się z dokładnie tego samego powodu

Program dlatego wykłada się na tym konkretnym elemencie, bo w tym folderze pub znajduje się link (…/przyklady) a nie faktyczny folder. Na szczęście da się to obejść. Pierwszy znak każdego z tych łańcuchów otrzymanych z metody ListDirectoryDetails informuje o tym, czy dany element to plik (znak -), folder (litera d), czy też link (litera l). Ja w tej pierwszej wersji kodu rozpoznaje, czy dany element jest plikiem po tym, czy ma rozszerzenie (to jest oczywiście nie za dobre rozwiązanie, bo nie zawsze plik musi mieć rozszerzenie), ale że nie wiedziałem w jakiej postaci dostajesz wyniki z serwera ftp, no to inaczej tego nie robiłem (jasne, można rozpatrzyć wszystkie przypadki, ale to już baw się sam :slight_smile: ). Dziś (po filmie) albo jutro zaimplementuję Ci metodę ParseDirectoryDetailsList (aktualnie zwraca zawsze null-a) tak, żeby to działało. No chyba, że zdążysz to zrobić samemu. Zaznaczam, że może na tym trochę ucierpieć wydajność programu (przesyłanych będzie więcej danych).

W porównaniu z czym? libCURLem? XHR w JavaScript?

Jeszcze raz wrzucam wszystkie pliki. Zobacz, czy teraz będzie OK.

FTPClient.cs

using System;

using System.Text;

using System.IO;

using System.Net;


namespace FTPDemo

{

    public class FTPClient

    {

        public FTPClient(string userName, string password)

        {

            UserName = string.IsNullOrEmpty(userName) ? string.Empty : userName;

            Password = string.IsNullOrEmpty(password) ? string.Empty : password;

            KeepAlive = true;

            UsePassive = true;

            EnableSsl = false;

            Timeout = System.Threading.Timeout.Infinite;

        }


        public string UserName { get; set; }

        public string Password { get; set; }

        public bool KeepAlive { get; set; }

        public bool UsePassive { get; set; }

        public bool EnableSsl { get; set; }

        public int Timeout { get; set; }


        public FTPObject[] GetDirectoryList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectory);

                return list != null ? ParseDirectoryList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public FTPObject[] GetDirectoryDetailsList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectoryDetails);

                return list != null ? ParseDirectoryDetailsList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public string CombineUrl(string url1, string url2)

        {

            if (string.IsNullOrEmpty(url1))

                return url2;

            if (string.IsNullOrEmpty(url2))

                return url1;


            url1 = url1.TrimEnd(new char[] { '/' });

            url2 = url2.TrimStart(new char[] { '/' });


            return string.Format("{0}/{1}", url1, url2);

        }


        private FtpWebRequest GetRequest(string url)

        {

            try

            {

                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);

                request.Credentials = new NetworkCredential(UserName, Password);

                request.KeepAlive = KeepAlive;

                request.UsePassive = UsePassive;

                request.EnableSsl = EnableSsl;

                request.Timeout = Timeout;

                return request;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        private string[] GetList(string url, string method)

        {

            FtpWebResponse response = null;

            Stream responseStream = null;

            StreamReader reader = null;


            try

            {

                FtpWebRequest request = GetRequest(url);

                request.Method = method;


                response = (FtpWebResponse)request.GetResponse();

                responseStream = response.GetResponseStream();

                reader = new StreamReader(responseStream, true);


                string result = reader.ReadToEnd();

                return !string.IsNullOrEmpty(result) ? result.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

            finally

            {

                if (reader != null)

                    reader.Close();

                if (responseStream != null)

                    responseStream.Close();

                if (response != null)

                    response.Close();

            }

        }


        private FTPObject[] ParseDirectoryList(string url, string[] list)

        {

            FTPObject[] result = new FTPObject[list.Length];


            for (int i = 0; i < list.Length; i++)

            {

                FTPObject.FTPObjectType objectType = FTPObject.FTPObjectType.Directory;

                if (!string.IsNullOrEmpty(Path.GetExtension(list[i])))

                    objectType = FTPObject.FTPObjectType.File;


                FTPObject ftpObject = new FTPObject(url, list[i], objectType);

                result[i] = ftpObject;

            }


            return result;

        }


        private FTPObject[] ParseDirectoryDetailsList(string url, string[] list)

        {

            FTPObject[] result = new FTPObject[list.Length];


            for (int i = 0; i < list.Length; i++)

            {

                string mode = string.Empty;

                string links = string.Empty;

                string owner = string.Empty;

                string group = string.Empty;

                string size = string.Empty;

                string month = string.Empty;

                string dayOfMonth = string.Empty;

                string hourOrYear = string.Empty;

                string name = string.Empty;

                int startIndex = 0;

                int length = 0;

                int wordCounter = 1;

                for (int j = 0; j < list[i].Length; j++)

                {

                    if (!(list[i][j].CompareTo(' ') == 0 || list[i][j].CompareTo('\t') == 0) || wordCounter == 9)

                        length++;

                    else if (length > 0)

                    {

                        string s = list[i].Substring(startIndex, length);

                        switch (wordCounter)

                        {

                            case 1:

                                mode = s;

                                break;

                            case 2:

                                links = s;

                                break;

                            case 3:

                                owner = s;

                                break;

                            case 4:

                                group = s;

                                break;

                            case 5:

                                size = s;

                                break;

                            case 6:

                                month = s;

                                break;

                            case 7:

                                dayOfMonth = s;

                                break;

                            case 8:

                                hourOrYear = s;

                                break;

                        }


                        length = 0;

                        startIndex = j + 1;

                        wordCounter++;

                    }

                    else

                        startIndex = j + 1;

                }


                name = list[i].Substring(startIndex, length);


                FTPObject.FTPObjectType objectType = FTPObject.FTPObjectType.File;

                if (list[i].StartsWith("d"))

                    objectType = FTPObject.FTPObjectType.Directory;

                else if (list[i].StartsWith("l"))

                    objectType = FTPObject.FTPObjectType.Link;


                string hour = string.Empty;

                string year = hourOrYear;

                if (hourOrYear.Contains(":"))

                {

                    hour = hourOrYear;

                    year = DateTime.Now.Year.ToString();

                }


                StringBuilder dateBuilder = new StringBuilder();

                dateBuilder.Append(dayOfMonth);

                dateBuilder.Append(" ");

                dateBuilder.Append(month);

                dateBuilder.Append(" ");

                dateBuilder.Append(year);

                if (!string.IsNullOrEmpty(hour))

                {

                    dateBuilder.Append(" ");

                    dateBuilder.Append(hour);

                }

                DateTime lastModified = DateTime.Parse(dateBuilder.ToString());


                FTPObject ftpObject = new FTPObject(url, name, objectType, new FTPObjectDetails(int.Parse(size), lastModified));

                result[i] = ftpObject;

            }


            return result;

        }

    }

}

FTPObject.cs

using System;


namespace FTPDemo

{

    public class FTPObject

    {

        public FTPObject(string url, string name, FTPObjectType objectType)

        {

            Name = name;

            Url = url;

            ObjectType = objectType;

            Details = null;

        }


        public FTPObject(string url, string name, FTPObjectType objectType, FTPObjectDetails details)

        {

            Name = name;

            Url = url;

            ObjectType = objectType;

            Details = details;

        }


        public string Url { get; private set; }

        public string Name { get; private set; }

        public FTPObjectType ObjectType { get; private set; }

        public FTPObjectDetails Details { get; private set; }


        public enum FTPObjectType

        {

            Directory,

            File,

            Link

        }

    }

}

FTPObjectDetails.cs

using System;


namespace FTPDemo

{

    public class FTPObjectDetails

    {

        public FTPObjectDetails(int size, DateTime lastModified)

        {

            Size = size;

            LastModified = lastModified;

        }


        public int Size { get; private set; }

        public DateTime LastModified { get; private set; }

    }

}

Form1.cs

using System;

using System.Windows.Forms;


namespace FTPDemo

{

    public partial class Form1 : Form

    {

        FTPClient client = null;


        public Form1()

        {

            InitializeComponent();

        }


        private void Form1_Load(object sender, EventArgs e)

        {

            client = new FTPClient(ftpUserNameTextBox.Text, ftpPasswordTextBox.Text);

        }


        private void refreshButton_Click(object sender, EventArgs e)

        {

            Fill(ftpTreeView, ftpUrlTextBox.Text);

        }


        private void Fill(TreeView tree, string url)

        {

            tree.Nodes.Clear();


            FTPObject[] list = client.GetDirectoryDetailsList(url);

            if (list != null)

            {

                foreach (FTPObject o in list)

                {

                    TreeNode node = new TreeNode(o.Name);

                    node.Tag = o;

                    tree.Nodes.Add(node);


                    if (o.ObjectType == FTPObject.FTPObjectType.Directory)

                        Fill(node, client.CombineUrl(url, o.Name));

                }

            }

        }


        private void Fill(TreeNode parentNode, string url)

        {

            FTPObject[] list = client.GetDirectoryDetailsList(url);

            if (list != null)

            {

                foreach (FTPObject o in list)

                {

                    TreeNode node = new TreeNode(o.Name);

                    node.Tag = o;

                    parentNode.Nodes.Add(node);


                    if (o.ObjectType == FTPObject.FTPObjectType.Directory)

                        Fill(node, client.CombineUrl(url, o.Name));

                }

            }

        }

    }

}

W porównaniu z wersją “socketową”(własna implementacja, ale XHR z JS też był szybszy). *WebRequest nie jest bardzo wolne, ale ma problem “z pierwszym odpaleniem” - trzeba(lub: trzeba było, mogli coś zmienić w .NET Fx 4.0, używałem tego “za czasów” 3.5 albo nawet 3.0) trochę poczekać, by móc dostać response stream.

:arrow: matzu

Twój kod rzeczywiście jest o wiele, wiele bardziej wydajny, bo teraz program pobiera listę w ciągu max. minuty.

Ale muszę jeszcze trochę posiedzieć, żeby się w niego wgłębić, bo widzę, że Twój program jest właściwie napisany od nowa, a ja chciałbym jeszcze coś zmienić w swoim kodzie.

Co powoduje, że Twój program działa szybciej od mojego? Bo z tego co widzę, to Ty też używasz rekurencji

Minuta to w sumie wciąż jest dość długo. A z jakiego serwera ftp pobierasz te dane? Z tego helion-a?

Rekurencja musi być użyta, bo to jest jedyny sposób na pobranie tego drzewa katalogów. Szczerze to nie przyglądałem się temu Twojemu kodowi. Odpaliłem go tylko u siebie, żeby zobaczyć, co otrzymam. Wyniki nie do końca były poprawne, więc uznałem, że lepiej będzie napisać wszystko od nowa niż szukać błędu. Co się rzuca w oczy w tym Twoim kodzie to to, że nie zwalniasz zasobów za pomocą metody Close (zobacz co robię w metodzie GetList w sekcji finally) oraz to, że niepotrzebnie używałeś metody Substring i błędnie sprawdzałeś, czy dany element to plik. U Ciebie pętla szła od indeksu 0, a lepszym rozwiązaniem jest puścić pętlę od ostatniego indeksu. Sęk w tym, że to samo robi metoda Path.GetExtension, więc nie ma potrzeby pisać tego samemu.

Wciąż możesz poprawić wydajność tego mojego kodu np. poprzez wywoływanie metody void Fill(TreeNode parentNode, string url) w osobnych wątkach. Jeśli jednak korzystasz z serwera, na którym zgromadzone jest na prawdę dużo plików, to lepszym rozwiązaniem będzie zrobić to o czym wspominał Fiołek, czyli ściągać zawartość podfolderów na żądanie. Nie oznaczałoby to jakoś specjalnie dużo zmian w kodzie. Najlepiej byłoby przygotować do tego celu własną kontrolkę o nazwie powiedzmy FTPTreeView. Każdy node tego treeView przechowywałby pełne informacje o wyświetlanym elemencie. W sumie już to jest robione, bowiem we właściwości Tag każdego node-a umieszczony jest obiekt klasy FTPObject. Ta kontrolka FTPTreeView mogłaby mieć metodę do pobierania całego drzewa (czyli tą metodę, która już jest zaimplementowana) i metodę do pobierania danych na żądanie (przeciążyłbyś metodę OnBeforeExpand).

Dziś zauważyłem bug w tym moim kodzie związany z kodowaniem. Niestety obejście tego to nie jest prosta sprawa. Problem polega na tym, że gdy serwer ftp wysyła dane, to nie jest wiadomo przy użyciu jakiego kodowania te dane zostały zapisane. Szukałem bibliotek, które służą do wykrywania kodowania. Ostatecznie zdecydowałem się na http://code.google.com/p/ude/ (jak znajdziesz lepszą bibliotekę to daj znać). Pobierz sobie ten projekt, skompiluj i do referencji dodaj bibliotekę Ude.dll. Następnie podmień kod klasy FTPClient.cs na

using System;

using System.Text;

using System.IO;

using System.Net;

using Ude;


namespace FTPDemo

{

    public class FTPClient

    {

        public FTPClient(string userName, string password)

        {

            UserName = string.IsNullOrEmpty(userName) ? string.Empty : userName;

            Password = string.IsNullOrEmpty(password) ? string.Empty : password;

            KeepAlive = true;

            UsePassive = true;

            EnableSsl = false;

            Timeout = System.Threading.Timeout.Infinite;

        }


        public string UserName { get; set; }

        public string Password { get; set; }

        public bool KeepAlive { get; set; }

        public bool UsePassive { get; set; }

        public bool EnableSsl { get; set; }

        public int Timeout { get; set; }


        public FTPObject[] GetDirectoryList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectory);

                return list != null ? ParseDirectoryList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public FTPObject[] GetDirectoryDetailsList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectoryDetails);

                return list != null ? ParseDirectoryDetailsList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public string CombineUrl(string url1, string url2)

        {

            if (string.IsNullOrEmpty(url1))

                return url2;

            if (string.IsNullOrEmpty(url2))

                return url1;


            url1 = url1.TrimEnd(new char[] { '/' });

            url2 = url2.TrimStart(new char[] { '/' });


            return string.Format("{0}/{1}", url1, url2);

        }


        private FtpWebRequest GetRequest(string url)

        {

            try

            {

                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);

                request.Credentials = new NetworkCredential(UserName, Password);

                request.KeepAlive = KeepAlive;

                request.UsePassive = UsePassive;

                request.EnableSsl = EnableSsl;

                request.Timeout = Timeout;

                return request;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        private string[] GetList(string url, string method)

        {

            FtpWebResponse response = null;

            Stream responseStream = null;

            StreamReader reader = null;


            try

            {

                FtpWebRequest request = GetRequest(url);

                request.Method = method;


                response = (FtpWebResponse)request.GetResponse();

                responseStream = response.GetResponseStream();


                byte[] bytes;

                float confidence;

                Encoding encoding = DetectEncoding(responseStream, out bytes, out confidence);


                string result = encoding.GetString(bytes);

                return !string.IsNullOrEmpty(result) ? result.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

            finally

            {

                if (reader != null)

                    reader.Close();

                if (responseStream != null)

                    responseStream.Close();

                if (response != null)

                    response.Close();

            }

        }


        private Encoding DetectEncoding(Stream stream, out byte[] bytes, out float confidence)

        {

            MemoryStream memoryStream = new MemoryStream();

            stream.CopyTo(memoryStream);

            bytes = memoryStream.ToArray();


            return DetectEncoding(bytes, out confidence);

        }


        private Encoding DetectEncoding(byte[] bytes, out float confidence)

        {

            CharsetDetector charsetDetector = new CharsetDetector();

            charsetDetector.Feed(bytes, 0, bytes.Length);

            charsetDetector.DataEnd();


            Encoding result = Encoding.Default;

            confidence = 0f;

            if (!string.IsNullOrEmpty(charsetDetector.Charset))

            {

                result = Encoding.GetEncoding(charsetDetector.Charset);

                confidence = charsetDetector.Confidence;

            }


            return result;

        }


        private FTPObject[] ParseDirectoryList(string url, string[] list)

        {

            FTPObject[] result = new FTPObject[list.Length];


            for (int i = 0; i < list.Length; i++)

            {

                FTPObject.FTPObjectType objectType = FTPObject.FTPObjectType.Directory;

                if (!string.IsNullOrEmpty(Path.GetExtension(list[i])))

                    objectType = FTPObject.FTPObjectType.File;


                FTPObject ftpObject = new FTPObject(url, list[i], objectType);

                result[i] = ftpObject;

            }


            return result;

        }


        private FTPObject[] ParseDirectoryDetailsList(string url, string[] list)

        {

            FTPObject[] result = new FTPObject[list.Length];


            for (int i = 0; i < list.Length; i++)

            {

                string mode = string.Empty;

                string links = string.Empty;

                string owner = string.Empty;

                string group = string.Empty;

                string size = string.Empty;

                string month = string.Empty;

                string dayOfMonth = string.Empty;

                string hourOrYear = string.Empty;

                string name = string.Empty;

                int startIndex = 0;

                int length = 0;

                int wordCounter = 1;

                for (int j = 0; j < list[i].Length; j++)

                {

                    if (!(list[i][j].CompareTo(' ') == 0 || list[i][j].CompareTo('\t') == 0) || wordCounter == 9)

                        length++;

                    else if (length > 0)

                    {

                        string s = list[i].Substring(startIndex, length);

                        switch (wordCounter)

                        {

                            case 1:

                                mode = s;

                                break;

                            case 2:

                                links = s;

                                break;

                            case 3:

                                owner = s;

                                break;

                            case 4:

                                group = s;

                                break;

                            case 5:

                                size = s;

                                break;

                            case 6:

                                month = s;

                                break;

                            case 7:

                                dayOfMonth = s;

                                break;

                            case 8:

                                hourOrYear = s;

                                break;

                        }


                        length = 0;

                        startIndex = j + 1;

                        wordCounter++;

                    }

                    else

                        startIndex = j + 1;

                }


                name = list[i].Substring(startIndex, length);


                FTPObject.FTPObjectType objectType = FTPObject.FTPObjectType.File;

                if (list[i].StartsWith("d"))

                    objectType = FTPObject.FTPObjectType.Directory;

                else if (list[i].StartsWith("l"))

                    objectType = FTPObject.FTPObjectType.Link;


                string hour = string.Empty;

                string year = hourOrYear;

                if (hourOrYear.Contains(":"))

                {

                    hour = hourOrYear;

                    year = DateTime.Now.Year.ToString();

                }


                StringBuilder dateBuilder = new StringBuilder();

                dateBuilder.Append(dayOfMonth);

                dateBuilder.Append(" ");

                dateBuilder.Append(month);

                dateBuilder.Append(" ");

                dateBuilder.Append(year);

                if (!string.IsNullOrEmpty(hour))

                {

                    dateBuilder.Append(" ");

                    dateBuilder.Append(hour);

                }

                DateTime lastModified = DateTime.Parse(dateBuilder.ToString());


                FTPObject ftpObject = new FTPObject(url, name, objectType, new FTPObjectDetails(int.Parse(size), lastModified));

                result[i] = ftpObject;

            }


            return result;

        }

    }

}

Dodane 11.07.2011 (Pn) 3:47 – Doszły kolejne zmiany. Na formę zamiast kontrolki TreeView wrzuć sobie kontrolkę FTPTreeView (będzie widoczna, gdy skompilujesz projekt). Usuń klasy FTPObject oraz FTPObjectDetails, bo zmieniłem ich nazwy i trochę zmieniłem kod. Wszystkie wymagane pliki umieszczam poniżej. BTW zauważ jak skrócił się kod klasy Form1 i sprawdź czy wydajność się poprawiła. FTPClient.cs

using System;

using System.Text;

using System.IO;

using System.Net;

using Ude;


namespace FTPDemo.Core

{

    public class FTPClient

    {

        public FTPClient(string userName, string password)

        {

            UserName = string.IsNullOrEmpty(userName) ? string.Empty : userName;

            Password = string.IsNullOrEmpty(password) ? string.Empty : password;

            KeepAlive = true;

            UsePassive = true;

            EnableSsl = false;

            Timeout = System.Threading.Timeout.Infinite;

        }


        public string UserName { get; set; }

        public string Password { get; set; }

        public bool KeepAlive { get; set; }

        public bool UsePassive { get; set; }

        public bool EnableSsl { get; set; }

        public int Timeout { get; set; }


        public FTPElement[] GetDirectoryList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectory);

                return list != null ? ParseDirectoryList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public FTPElement[] GetDirectoryDetailsList(string url)

        {

            try

            {

                string[] list = GetList(url, WebRequestMethods.Ftp.ListDirectoryDetails);

                return list != null ? ParseDirectoryDetailsList(url, list) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        public string CombineUrl(string url1, string url2)

        {

            if (string.IsNullOrEmpty(url1))

                return url2;

            if (string.IsNullOrEmpty(url2))

                return url1;


            url1 = url1.TrimEnd(new char[] { '/' });

            url2 = url2.TrimStart(new char[] { '/' });


            return string.Format("{0}/{1}", url1, url2);

        }


        private FtpWebRequest GetRequest(string url)

        {

            try

            {

                FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);

                request.Credentials = new NetworkCredential(UserName, Password);

                request.KeepAlive = KeepAlive;

                request.UsePassive = UsePassive;

                request.EnableSsl = EnableSsl;

                request.Timeout = Timeout;

                return request;

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }


        private string[] GetList(string url, string method)

        {

            FtpWebResponse response = null;

            Stream responseStream = null;

            StreamReader reader = null;


            try

            {

                FtpWebRequest request = GetRequest(url);

                request.Method = method;


                response = (FtpWebResponse)request.GetResponse();

                responseStream = response.GetResponseStream();


                byte[] bytes;

                float confidence;

                Encoding encoding = DetectEncoding(responseStream, out bytes, out confidence);


                string result = encoding.GetString(bytes);

                return !string.IsNullOrEmpty(result) ? result.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries) : null;

            }

            catch (Exception ex)

            {

                throw ex;

            }

            finally

            {

                if (reader != null)

                    reader.Close();

                if (responseStream != null)

                    responseStream.Close();

                if (response != null)

                    response.Close();

            }

        }


        private Encoding DetectEncoding(Stream stream, out byte[] bytes, out float confidence)

        {

            MemoryStream memoryStream = new MemoryStream();

            stream.CopyTo(memoryStream);

            bytes = memoryStream.ToArray();


            return DetectEncoding(bytes, out confidence);

        }


        private Encoding DetectEncoding(byte[] bytes, out float confidence)

        {

            CharsetDetector charsetDetector = new CharsetDetector();

            charsetDetector.Feed(bytes, 0, bytes.Length);

            charsetDetector.DataEnd();


            Encoding result = Encoding.Default;

            confidence = 0f;

            if (!string.IsNullOrEmpty(charsetDetector.Charset))

            {

                result = Encoding.GetEncoding(charsetDetector.Charset);

                confidence = charsetDetector.Confidence;

            }


            return result;

        }


        private FTPElement[] ParseDirectoryList(string url, string[] list)

        {

            FTPElement[] result = new FTPElement[list.Length];


            for (int i = 0; i < list.Length; i++)

            {

                FTPElement.FTPElementType elementType = FTPElement.FTPElementType.Directory;

                if (!string.IsNullOrEmpty(Path.GetExtension(list[i])))

                    elementType = FTPElement.FTPElementType.File;


                FTPElement element = new FTPElement(list[i], CombineUrl(url, list[i]), elementType);

                result[i] = element;

            }


            return result;

        }


        private FTPElement[] ParseDirectoryDetailsList(string url, string[] list)

        {

            FTPElement[] result = new FTPElement[list.Length];


            for (int i = 0; i < list.Length; i++)

            {

                string mode = string.Empty;

                string links = string.Empty;

                string owner = string.Empty;

                string group = string.Empty;

                string size = string.Empty;

                string month = string.Empty;

                string dayOfMonth = string.Empty;

                string hourOrYear = string.Empty;

                string name = string.Empty;

                int startIndex = 0;

                int length = 0;

                int wordCounter = 1;

                for (int j = 0; j < list[i].Length; j++)

                {

                    if (!(list[i][j].CompareTo(' ') == 0 || list[i][j].CompareTo('\t') == 0) || wordCounter == 9)

                        length++;

                    else if (length > 0)

                    {

                        string s = list[i].Substring(startIndex, length);

                        switch (wordCounter)

                        {

                            case 1:

                                mode = s;

                                break;

                            case 2:

                                links = s;

                                break;

                            case 3:

                                owner = s;

                                break;

                            case 4:

                                group = s;

                                break;

                            case 5:

                                size = s;

                                break;

                            case 6:

                                month = s;

                                break;

                            case 7:

                                dayOfMonth = s;

                                break;

                            case 8:

                                hourOrYear = s;

                                break;

                        }


                        length = 0;

                        startIndex = j + 1;

                        wordCounter++;

                    }

                    else

                        startIndex = j + 1;

                }


                name = list[i].Substring(startIndex, length);


                FTPElement.FTPElementType elementType = FTPElement.FTPElementType.File;

                if (list[i].StartsWith("d"))

                    elementType = FTPElement.FTPElementType.Directory;

                else if (list[i].StartsWith("l"))

                    elementType = FTPElement.FTPElementType.Link;


                string hour = string.Empty;

                string year = hourOrYear;

                if (hourOrYear.Contains(":"))

                {

                    hour = hourOrYear;

                    year = DateTime.Now.Year.ToString();

                }


                StringBuilder dateBuilder = new StringBuilder();

                dateBuilder.Append(dayOfMonth);

                dateBuilder.Append(" ");

                dateBuilder.Append(month);

                dateBuilder.Append(" ");

                dateBuilder.Append(year);

                if (!string.IsNullOrEmpty(hour))

                {

                    dateBuilder.Append(" ");

                    dateBuilder.Append(hour);

                }

                DateTime lastModified = DateTime.Parse(dateBuilder.ToString());


                FTPElement element = new FTPElement(name, CombineUrl(url, name), elementType, new FTPElementDetails(int.Parse(size), lastModified));

                result[i] = element;

            }


            return result;

        }

    }

}

FTPElement.cs

using System;


namespace FTPDemo.Core

{

    public class FTPElement

    {

        public FTPElement(string name, string url, FTPElementType elementType)

        {

            Name = name;

            Url = url;

            ElementType = elementType;

            ElementDetails = null;

        }


        public FTPElement(string name, string url, FTPElementType elementType, FTPElementDetails elementDetails)

        {

            Name = name;

            Url = url;

            ElementType = elementType;

            ElementDetails = elementDetails;

        }


        public string Url { get; private set; }

        public string Name { get; private set; }

        public FTPElementType ElementType { get; private set; }

        public FTPElementDetails ElementDetails { get; private set; }

        public bool IsDirectory

        {

            get { return (ElementType == FTPElementType.Directory); }

        }

        public bool IsFile 

        {

            get { return (ElementType == FTPElementType.File); }

        }

        public bool IsLink

        {

            get { return (ElementType == FTPElementType.Link); }

        }


        public enum FTPElementType

        {

            Directory,

            File,

            Link

        }

    }

}

FTPElementDetails.cs

using System;


namespace FTPDemo.Core

{

    public class FTPElementDetails

    {

        public FTPElementDetails(int size, DateTime lastModified)

        {

            Size = size;

            LastModified = lastModified;

        }


        public int Size { get; private set; }

        public DateTime LastModified { get; private set; }

    }

}

FTPTreeNode.cs

using System;

using System.Windows.Forms;

using FTPDemo.Core;


namespace FTPDemo.UI

{

    public class FTPTreeNode : TreeNode

    {

        public FTPTreeNode()

            : base()

        {

            Element = null;

            ChildrenLoaded = false;

        }


        public FTPTreeNode(FTPElement element)

            : base(element.Name)

        {

            Element = element;

            ChildrenLoaded = false;

        }


        public FTPTreeNode(FTPElement element, int imageIndex, int selectedImageIndex)

            : base(element.Name, imageIndex, selectedImageIndex)

        {

            Element = element;

            ChildrenLoaded = false;

        }


        public FTPElement Element { get; private set; }

        public bool ChildrenLoaded { get; set; }

    }

}

FTPTreeView.cs

using System;

using System.Windows.Forms;

using System.ComponentModel;

using FTPDemo.Core;


namespace FTPDemo.UI

{

    [DefaultProperty("Url")]

    public class FTPTreeView : TreeView

    {

        private const string _DEFAULT_USER_NAME = "";

        private const string _DEFAULT_PASSWORD = "";

        private const string _CATEGORY_NAME = "FTP Settings";


        private FTPClient client;

        private bool branchOnly;


        public FTPTreeView()

            : base()

        {

            client = new FTPClient(_DEFAULT_USER_NAME, _DEFAULT_PASSWORD);

            branchOnly = false;

        }


        [Bindable(true)]

        [Category(_CATEGORY_NAME)]

        [Description("")]

        [DefaultValue(_DEFAULT_USER_NAME)]

        [Localizable(true)]

        public string UserName

        {

            get { return client.UserName; }

            set { client.UserName = value; }

        }


        [Bindable(true)]

        [Category(_CATEGORY_NAME)]

        [Description("")]

        [DefaultValue(_DEFAULT_PASSWORD)]

        [Localizable(true)]

        public string Password

        {

            get { return client.Password; }

            set { client.Password = value; }

        }


        protected override void OnBeforeExpand(TreeViewCancelEventArgs e)

        {

            FTPTreeNode node = (FTPTreeNode)e.Node;

            if (node != null && !node.ChildrenLoaded)

            {

                node.Nodes.Clear();

                Load(node, node.Element.Url);

                node.ChildrenLoaded = true;

            }


            base.OnBeforeExpand(e);

        }


        public void Clear()

        {

            Nodes.Clear();

        }


        public void Load(string url, bool branchOnly)

        {

            this.branchOnly = branchOnly;

            Load(null, url);

        }


        private void Load(TreeNode parentNode, string url)

        {

            FTPElement[] list = client.GetDirectoryDetailsList(url);

            if (list != null)

            {

                foreach (FTPElement element in list)

                {

                    FTPTreeNode node = new FTPTreeNode(element);

                    if (parentNode != null)

                        parentNode.Nodes.Add(node);

                    else

                        Nodes.Add(node);


                    if (element.IsDirectory)

                    {

                        if (!branchOnly)

                        {

                            Load(node, node.Element.Url);

                            node.ChildrenLoaded = true;

                        }

                        else

                        {

                            FTPTreeNode emptyNode = new FTPTreeNode();

                            node.Nodes.Add(emptyNode);

                            node.ChildrenLoaded = false;

                        }

                    }

                }

            }

        }

    }

}

Form1.cs

using System;

using System.Windows.Forms;


namespace FTPDemo

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }


        private void refreshButton_Click(object sender, EventArgs e)

        {

            ftpTreeView.UserName = ftpUserNameTextBox.Text;

            ftpTreeView.Password = ftpPasswordTextBox.Text;


            ftpTreeView.Clear();

            ftpTreeView.Load(ftpUrlTextBox.Text, true);

        }

    }

}

Chcę to zrozumieć: twierdzisz, że napisana w kodzie niezarządzanym, na socketach obsługa HTTP będzie dużo wydajniejsza? Na jakiej podstawie? Wąskim gardłem jest IO po sieci, nie przetwarzanie danych. Mówimy o różnicy poniżej milisekundy, podczas gdy każde zapytanie to dziesiątki milisekund (lub znacznie, znacznie więcej). Zysk rzędu 1% to żaden zysk.

To jest problem aplikacji w .NET w ogóle, ale jeśli wykonujesz więcej niż jedno zapytanie (tak jak tutaj), to koszt odpalenia jest znikomy w skali czasu działania programu. Szczególnie że np. aplikacja z linii poleceń ma zależność od dwóch tylko asembli (core z System i System.Net z WebRequest). To, co piszesz, mocno śmierdzi optymalizacją czegoś, co wcale nie jest problemem.

Nie, chodziło mi o implementację opartą na TcpClient lub bezpośrednio na Socket.

Znalazłem chwilę wolnego czasu i sprawdziłem, czy moje przypuszczenia są prawdziwe. Okazało się, że nie są. Moja implementacja klienta FTP(tylko tryb pasywny, on był wąskim gardłem - nie do końca miałem pomysł jak go sensownie zaimplementować, do tego trochę kulała, ale nie na tyle by pomiar nie miał większego sensu) oparta na TcpClient(tak matzu , TcpClient wystarczy :wink: ) okazała się być porównywalnej szybkości do metody opartej o FtpWebRequest.

Testy przeprowadzałem na serwerze FTP firmy Webio, które działają szybko. Na test składało się wylistowanie wszystkich plików, folderów i podfolderów z jednego określonego folderu(struktura to mniej więcej coś takiego: 3 podfoldery, jeden z nich z 8 plikami, jeden pusty, ostatni z jednym podfolderem, w którym to znajdował się jeden plik).

Dodatkowo zrewidowałem swoją aplikację wykorzystującą HttpWebRequest - okazało się, że strzeliłem babola w jednym miejscu i dlatego działało dość opornie.

Tak więc przepraszam wszystkich za wprowadzenie w błąd i dziękuję Ci Ryan za wyprowadzenie z błędu.