Coś musiałeś źle zaimplementować. FtpWebRequest nie zachowuje informacji o stanie, a więc za każdym razem (tzn. przy okazji wysyłania każdego polecenia) tworzone jest nowe połączenie z serwerem (co trwa stosunkowo długo). Implementując własne rozwiązanie możesz tworzyć jedno połączenie i w obrębie tego połączenia wysyłać polecenie CWD, służące do zmiany aktywnego katalogu (o czym zresztą już pisałem), co powinno być (choć faktycznie może się okazać, że wcale nie jest ) szybsze. Nie zauważysz jednak znaczących różnic na tak niedużej strukturze katalogów, na której testowałeś. Spróbuj pobrać strukturę katalogów z np. tego serwera ftp://ftp.mozilla.org/. Choć oczywiście to już teraz tylko rozważania teoretyczne, bo wydaje mi się, że zaprezentowane przeze mnie powyżej rozwiązanie jest wystarczające (jeśli się mylę to oczywiście napisz).
@eureka 170
Zmieniłem trochę kod kontrolki, tzn. udostępniłem kilka dodatkowych właściwości na zewnątrz (nic poza tym). Udało Ci się uruchomić całość?
FTPTreeView.cs
using System;
using System.Windows.Forms;
using System.ComponentModel;
using FTPDemo.Core;
namespace FTPDemo.UI
{
[DefaultProperty("UserName")]
public class FTPTreeView : TreeView
{
private const string _CATEGORY_NAME = "FTP Settings";
private const string _DEFAULT_USER_NAME = "";
private const string _DEFAULT_PASSWORD = "";
private const bool _DEFAULT_KEEP_ALIVE = true;
private const bool _DEFAULT_USE_PASSIVE = true;
private const bool _DEFAULT_ENABLE_SSL = false;
private const int _DEFAULT_TIMEOUT = System.Threading.Timeout.Infinite;
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; }
}
[Bindable(true)]
[Category(_CATEGORY_NAME)]
[Description("")]
[DefaultValue(_DEFAULT_KEEP_ALIVE)]
[Localizable(true)]
public bool KeepAlive
{
get { return client.KeepAlive; }
set { client.KeepAlive = value; }
}
[Bindable(true)]
[Category(_CATEGORY_NAME)]
[Description("")]
[DefaultValue(_DEFAULT_USE_PASSIVE)]
[Localizable(true)]
public bool UsePassive
{
get { return client.UsePassive; }
set { client.UsePassive = value; }
}
[Bindable(true)]
[Category(_CATEGORY_NAME)]
[Description("")]
[DefaultValue(_DEFAULT_ENABLE_SSL)]
[Localizable(true)]
public bool EnableSsl
{
get { return client.EnableSsl; }
set { client.EnableSsl = value; }
}
[Bindable(true)]
[Category(_CATEGORY_NAME)]
[Description("")]
[DefaultValue(_DEFAULT_TIMEOUT)]
[Localizable(true)]
public int Timeout
{
get { return client.Timeout; }
set { client.Timeout = 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;
}
}
}
}
}
}
}
Wszystko jest dobrze(dla testów) zaimplementowane, naprawdę FtpWebRequest, jeśli ustawimy KeepAlive na true, ZACHOWUJE połączenie z serwerem, nawet gdy zmieniamy Uri(tworząc nowe FtpWebRequest). Niestety, nie ma dostępu do kodów źródłowych System.Net, więc nie można tego prosto sprawdzić. Polecam jednak dość prosty test - dwa polecenia przez FtpWebRequest - raz wysyłamy je z KeepAlive=true, potem z false i sprawdzamy w logach co się działo.
EDIT:
@down: tak, wiem, że można to zrobić bez dostępu do oficjalnego kodu źródłowego, lecz mimo wszystko wydaje mi się, że sposób z logami jest prostszy
Zasadniczo nie potrzeba dostępu do kodu źródłowego System.Net, żeby to sprawdzić. Ale masz rację co do tego że, połączenie z serwerem FTP w sytuacji ustawienia KeepAlive na true jest utrzymywane (przepraszam za błąd). IMO opis odnośnie tej właściwości w dokumentacji msdn jest nie dość szczegółowy. Wracając do sedna sprawy … skoro połączenie jest utrzymywane, to nie ma potrzeby wykonywania własnej implementacji. Wzrost wydajności w porównaniu do rozwiązania opartego o FtpWebRequest mógł być widoczny właśnie tylko dzięki temu, że utrzymywane by było stałe połączenie z serwerem i można by było wykonywać polecenia CWD, ale jak widać jest to już robione za nas.
Oto krótki log dla potomnych z KeepAlive ustawionym na true (to był dobry pomysł z analizą tych logów):
W sytuacji ustawienia KeepAlive na false połączenie jest zamykane za pomocą polecenia QUIT w momencie, gdy wywołania zostanie metoda Close klasy FtpWebResponse.
Wiedziałem o tym. Sprawdziłem wydajność curl vs WebClient i różnica była zależna jedynie od fluktuacji przepustowości sieci w momencie uruchamiania testu. Dlatego tak parłem na ten temat.
Nie no usuwać tego nie możesz Nie bez powodu dodałem tą przestrzeń nazw. Pisałem, żebyś ściągnął sobie tamten projekt ze strony google i skompilował. Anyway tu masz tą bibliotekę http://www.speedyshare.com/files/29488958/Ude.dll. Dodaj ją do referencji projektu, a kod zostaw tak jak był.
Hmm… ściągnąłem tą bibliotekę, wkleiłem kod i wyświetla mi się błąd:
Error 1 ‘System.IO.Stream’ does not contain a definition for ‘CopyTo’ and no extension method ‘CopyTo’ accepting a first argument of type ‘System.IO.Stream’ could be found (are you missing a using directive or an assembly reference?)
Dotyczy to klasy FTPClient.cs. Poza tym w Form1.cs musiałem uworzyć obiekt typu FTPTreeView, bo w Twoim kodzie nie wiadomo skąd bierze się ftpTreeView i kompilator przez to też zwraca błąd
Miałeś sobie wrzucić na formę kontrolkę FTPTreeView zamiast TreeView. Po wrzuceniu na formę zmień jej nazwę (właściwość Name) na ftpTreeView i będziesz to miał. Ta kontrolka FTPTreeView powinna się pojawić w Toolbox-ie (na samej górze) w sekcji Components.
Co do tego komunikatu błędu … Projekt kompilujesz przy użyciu .NET Framework 4? A jeśli nie, to czy możesz zmienić na .NET Framework 4?
Ok, udało mi się uruchomić, jest jeszcze szybciej, teraz pobiera mi to w czasie poniżej 10s.
Mam jeszcze problem… jak napisać funkcję podwójnego kliknięcia na TreeView - to jeszcze dotyczy mojego starego programu
Znalazłem stronę http://www.dotnetperls.com/treeview . Próbuję to zrobić to tak jak oni, ale “To add the MouseDoubleClick event handler, right-click the TreeView in the designer and select Properties. Then, select “MouseDoubleClick” and click twice on that entry”. Gdzie w Properties jest opcja MouseDoubleClick, bo ja tego nie widzę?