2013-07-03 19 views
8

Sto cercando di creare un metodo semplice per scaricare un file da un FTP utilizzando FtpWebRequest con il metodo WebRequestMethods.Ftp.DownloadFile. Il problema è che non voglio visualizzare i progressi del download e quindi ho bisogno di conoscere la dimensione del file per poter calcolare la percentuale trasferita. Ma quando chiamo GetResponse in FtpWebRequest il membro ContentLength è -1.Riutilizzo di FtpWebRequest

OK - in modo da ottenere la dimensione del file in anticipo utilizzando il metodo WebRequestMethods.Ftp.GetFileSize. Nessun problema. Quindi, dopo aver ottenuto le dimensioni, scarica il file.

Questo è dove appare il problema in questione ...

Dopo aver ottenuto la dimensione cerco di riutilizzare il FtpWebRequest e reimposta il metodo per WebRequestMethods.Ftp.DownloadFile. Ciò causa un System.InvalidOperationException che dice qualcosa come "Impossibile eseguire questa azione dopo aver inviato la richiesta." (potrebbe non essere la formulazione esatta - tradotta da quella che ricevo in svedese).

Ho trovato altrove che finché ho impostato la proprietà KeepAlive su true, non importa, la connessione è mantenuta attiva. Questo è quello che non capisco ... L'unico oggetto che ho creato è il mio oggetto FtpWebRequest. E se ne creo un altro, come può sapere quale connessione usare? E quali credenziali?

pseudo codice:

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 

Ora ho la dimensione del file. Quindi è il momento di scaricare il file. Il metodo Setting sa causa l'eccezione sopra menzionata. Così creo un nuovo FtpWebRequest? O c'è comunque un modo per reimpostare la richiesta da riutilizzare? (La chiusura della risposta non ha fatto alcuna differenza)

Non capisco come andare avanti senza ricreare l'oggetto. Potrei farlo, ma non mi sembra giusto. Quindi sto postando qui nella speranza di trovare il modo corretto di farlo.

Ecco il codice (non funzionante) (Gli ingressi sono Suri, sDiskName, suser e 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(); 

Spero che qualcuno possa spiegare come questo dovrebbe funzionare. Grazie in anticipo.

+0

Almeno con HTTPWebRequests - è sempre necessario crearne uno nuovo. Supponiamo che lo stesso problema qui. – Hikiko

+0

'FtpWebRequest' è utile per effettuare richieste singole. Vuoi un client FTP, che connetta e mantenga una connessione persistente fino alla chiusura. 'System.Net.FtpClient' sembra ben considerato: http://netftp.codeplex.com/ –

risposta

6

Non penso che verrà risposto così sto "chiudendolo" dicendoti come l'ho risolto.

Beh, non l'ho davvero risolto. Ho comunque testato il download ricreando il FtpWebRequest e ho notato che sul server FTP si comportava come volevo, cioè un solo accesso e quindi l'esecuzione sequenziale delle mie richieste.

questo è come il codice di ottenere la dimensione del file e avviare il download finito:

// 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(); 

Quindi nessuna risposta su se è possibile azzerare il FtpWebRequest per il riutilizzo. Ma almeno so che non vengono trasferite informazioni ridondanti.

Grazie a tutti quelli che si sono interessati e hanno passato il tempo a pensare a una risposta.

+0

Questa risposta http://stackoverflow.com/a/7132428/2170171 fa luce su come dovrebbe funzionare, quindi in pratica stai facendo la cosa giusta. E questo http://stackoverflow.com/a/9666598/2170171 mostra come tracciare la richiesta FTP, così puoi vedere che la connessione è veramente riutilizzata tra le richieste – Lanorkin

0

Probabilmente vorrai utilizzare il metodo Async. Ecco il link al documento MSDN.

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

GetResponseAsync() 

che manterrà la vostra applicazione di bloccarsi anche, in modo da non dover usare

Application.DoEvents(); 

Si potrebbe anche guardare eventualmente utilizzando una libreria FTP alternativo. imo il FtpWebRequest non è esattamente la migliore classe ftp. Una rapida ricerca ha aperto questa libreria. Ftp non è stateless come HTTP. Preferisco le librerie che consentono di creare un client, aprire una connessione e mantenere viva la connessione.

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

Ecco il codice exacmple ho trovato

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

La fonte è anche lì, in modo che sarebbe in grado di collegare possibilmente alcuni eventi per il processo di lettura per il monitoraggio l'avanzamento del download.

+0

In questo caso sono un po 'riluttante a usare prodotti di terze parti. E sono ben consapevole del funzionamento interno di FTP poiché ho scritto sia i server che le librerie client da zero. Quello che sto cercando qui è sapere se è possibile fare con 'FtpWebRequest'. In caso contrario, credo che andrò con l'approccio Asynch (che ho disattivato in precedenza a causa della maggiore complessità del codice). Un po 'di vergogna però complicarlo in questo modo, quando non sono veramente dopo un comportamento asincrono, a parte l'aggiornamento della barra di avanzamento ... Grazie comunque. – ClasG

3

FtpWebRequest può essere utilizzato per una sola richiesta, come ottenere le dimensioni del file o scaricare il file, ma non entrambi. Devi creare 2 FtpWebRequests. Dietro la scena, FtpWebRequest nota che è lo stesso URL e le stesse credenziali e riutilizzerà la stessa connessione ftp senza chiuderla, a condizione che IsKeepAlieve sia true, che è l'impostazione predefinita.

Questo è un triste esempio di progettazione errata di Microsoft. Invece di lasciarci aprire e chiudere esplicitamente una connessione, vogliono farlo automaticamente per noi e confondere tutti.

+0

Giusto per chiarire, Non è possibile con FtpWebRequest inviare 10 file SENZA l'accesso 10 volte? –