2013-07-03 15 views
8

Próbuję wykonać prostą metodę pobrania pliku z FTP przy użyciu FtpWebRequest za pomocą metody WebRequestMethods.Ftp.DownloadFile. Problem polega na tym, że nie chcę wyświetlać postępu pobierania i dlatego muszę znać rozmiar pliku, aby móc obliczyć procent przeniesiony. Ale gdy zadzwonię pod numer GetResponse w FtpWebRequest, członkiem ContentLength jest -1.Ponowne użycie FtpWebRequest

OK - otrzymam z góry rozmiar pliku przy użyciu metody WebRequestMethods.Ftp.GetFileSize. Nie ma problemu. Następnie po uzyskaniu rozmiaru pobieram plik.

tym miejscu pojawia się problem w kwestii ...

Po uzyskaniu rozmiaru próbie ponownego użycia FtpWebRequest i resetuje metodę WebRequestMethods.Ftp.DownloadFile. Powoduje to, że System.InvalidOperationException mówi coś w stylu "Nie można wykonać tej czynności po wysłaniu żądania." (może nie być dokładnym sformułowaniem - przetłumaczonym z tego, które dostaję po szwedzku).

Znalazłem gdzie indziej, że dopóki ustawiam właściwość KeepAlive na true, nie ma to znaczenia, połączenie jest aktywne. Tego nie rozumiem ... Jedynym obiektem, który utworzyłem jest mój obiekt FtpWebRequest. A jeśli utworzę inny, jak on może wiedzieć, z jakiego połączenia korzystać? I jakie referencje?

Pseudo kod:

Create FtpWebRequest 
Set Method property to GetFileSize 
Set KeepAlive property to true 
Set Credentials property to new NetworkCredential(...) 
Get FtpWebResponse from the request 
Read and store ContentLength 

Teraz mam rozmiar pliku. Czas więc pobrać plik. Ustawienie Metoda wiem powoduje wyjątek wspomniany powyżej. Czy mogę utworzyć nowy FtpWebRequest? A może zresetować prośbę do ponownego użycia? (Zamknięcie odpowiedzi nie zrobiło żadnej różnicy.)

Nie rozumiem, jak posuwać się naprzód bez ponownego tworzenia obiektu. Mógłbym to zrobić, ale po prostu nie czuję się dobrze. Więc zamieszczam tutaj w nadziei, że znajdę właściwy sposób na zrobienie tego.

Oto (bez pracy) Kod (Wejścia są Suri, sDiskName, suser i spwd.):

FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(sURI); 
request.Method = WebRequestMethods.Ftp.GetFileSize; 
request.Credentials = new NetworkCredential(sUser, sPwd); 
request.UseBinary = true; 
request.UsePassive = true; 
request.KeepAlive = true; 

FtpWebResponse resp = (FtpWebResponse)request.GetResponse(); 
int contLen = (int)resp.ContentLength; 
resp.Close(); 

request.Method = WebRequestMethods.Ftp.DownloadFile; 

resp = (FtpWebResponse)request.GetResponse(); 

Stream inStr = resp.GetResponseStream(); 
byte[] buff = new byte[16384]; 

sDiskName = Environment.ExpandEnvironmentVariables(sDiskName); 
FileStream file = File.Create(sDiskName); 

int readBytesCount; 
int readTotal=0; 

while ((readBytesCount = inStr.Read(buff, 0, buff.Length)) > 0) 
{ 
    readTotal += readBytesCount; 
    toolStripProgressBar1.Value = 100*readTotal/contLen; 
    Application.DoEvents(); 
    file.Write(buff, 0, readBytesCount); 
} 
file.Close(); 

Mam nadzieję, że ktoś może wyjaśnić, jak to ma działać. Z góry dziękuję.

+0

Przynajmniej za pomocą HTTPWebRequests - zawsze musisz utworzyć nowy. Załóżmy, że ten sam problem tutaj. – Hikiko

+0

'FtpWebRequest' jest dobry do robienia pojedynczych żądań. Chcesz klienta FTP, który łączy i utrzymuje stałe połączenie, dopóki go nie zamkniesz. "System.Net.FtpClient" wydaje się być dobrze traktowany: http://netftp.codeplex.com/ –

Odpowiedz

6

Nie sądzę, że to będzie odpowiedź, więc "zamykam", mówiąc, jak to rozwiązałem.

Cóż, tak naprawdę to nie rozwiązałem. Testowałem jednak pobieranie, odtwarzając plik FtpWebRequest i zauważyłem, że na serwerze FTP zachowywał się tak, jak chciałem, tj. Tylko jedno logowanie, a następnie sekwencyjne wykonywanie moich żądań.

ten sposób kod coraz rozmiaru pliku i rozpoczęcie pobierania zakończył się:

// Start by fetching the file size 
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(sURI); 

request.Method = WebRequestMethods.Ftp.GetFileSize; 
NetworkCredential nc = new NetworkCredential(sUser, sPwd); 
request.Credentials = nc; 
request.UseBinary = true; 
request.UsePassive = true; 
request.KeepAlive = true; 

// Get the result (size) 
FtpWebResponse resp = (FtpWebResponse)request.GetResponse(); 
Int64 contLen = resp.ContentLength; 

// and now download the file 
request = (FtpWebRequest)FtpWebRequest.Create(sURI); 
request.Method = WebRequestMethods.Ftp.DownloadFile; 
request.Credentials = nc; 
request.UseBinary = true; 
request.UsePassive = true; 
request.KeepAlive = true; 

resp = (FtpWebResponse)request.GetResponse(); 

Więc nie ma odpowiedzi na to, czy jest możliwe, aby zresetować FtpWebRequest do ponownego użycia. Ale przynajmniej wiem, że nie ma żadnych nadmiarowych informacji.

Dziękuję wszystkim, którzy zainteresowali się i spędzili czas myśląc o odpowiedzi.

+0

Ta odpowiedź http://stackoverflow.com/a/7132428/2170171 rzuca trochę światła na to, jak to ma działać, więc w zasadzie robisz "właściwą" rzecz. Ten jeden http://stackoverflow.com/a/9666598/2170171 pokazuje, jak śledzić żądanie FTP, więc widzisz, że połączenie jest naprawdę ponownie wykorzystywane między żądaniami – Lanorkin

0

Prawdopodobnie będziesz chciał użyć metody Async. Oto link do dokumentu MSDN.

http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.aspx

GetResponseAsync() 

że zachowa swoją aplikację z blokowania się też, więc nie będzie musiał użyć

Application.DoEvents(); 

Można również spojrzeć na ewentualnie stosując alternatywną bibliotekę ftp. imo FtpWebRequest nie jest dokładnie najlepszą klasą ftp. Szybkie wyszukiwanie tej biblioteki. Ftp nie jest bezpaństwowcem takim jak HTTP. Preferuję biblioteki, które pozwalają tworzyć klienta, otwierać połączenia i utrzymywać połączenie przy życiu.

http://sanity-free.org/dist/NullFX.Net-binary.zip

Oto exacmple kod znalazłem

FtpClient client = 
    new FtpClient( 
     new IPEndPoint(IPAddress.Loopback, 21), 
     new NetworkCredential("test", "[email protected]") 
    ); 
client.Connect(); 
client.Download("testfile.zip", @"C:\downloads\testfile.zip"); 

Źródłem jest tam też, więc można ewentualnie dołączyć pewne zdarzenia w procesie odczytu do śledzenia postępu pobierania.

+0

W tym przypadku jestem trochę niechętny do korzystania z produktów stron trzecich. I jestem świadomy wewnętrznego działania FTP, ponieważ napisałem zarówno serwery, jak i biblioteki klientów od zera. Chcę tutaj wiedzieć, czy można to zrobić za pomocą 'FtpWebRequest'. Jeśli nie, to myślę, że pójdę z podejściem Asynch (które zrezygnowałem wcześniej ze względu na dodatkową złożoność kodu). Trochę wstydu jednak to komplikować, kiedy nie jestem tak naprawdę po asynchronicznym zachowaniu, poza aktualizacją paska postępu ... Dzięki i tak. – ClasG

3

Polecenie FtpWebRequest może być używane tylko dla jednego żądania, na przykład do pobrania rozmiaru pliku lub pobrania pliku, ale nie może obu naraz. Musisz utworzyć 2 FtpWebRequests. Za sceną FtpWebRequest zauważa, że ​​jest to ten sam adres URL i poświadczenia i ponownie użyje tego samego połączenia ftp bez zamykania go, tak długo, jak długo IsKeepAlieve ma wartość true, co jest ustawieniem domyślnym.

To smutny przykład złego projektu firmy Microsoft. Zamiast pozwalać nam jawnie otwierać i zamykać połączenie, chcą zrobić to automatycznie i mylić wszystkich.

+0

Wystarczy, aby wyjaśnić, czy nie jest możliwe, aby FtpWebRequest wysyłał pliki typu "powiedz 10" BEZ logowania 10 razy? –