2010-04-26 17 views
6

Vorrei utilizzare lo WebClient (o c'è un'altra opzione migliore?) Ma c'è un problema. Capisco che l'apertura del flusso richiede un po 'di tempo e questo non può essere evitato. Tuttavia, leggerlo richiede una quantità stranamente molto più lunga rispetto a leggerlo interamente immediatamente.Come scaricare il file in una stringa con callback progressivo?

C'è un modo migliore per farlo? Intendo due modi, stringere e archiviare. Progress è il mio delegato e funziona bene.


AGGIORNAMENTO QUINTO:

Infine, sono riuscito a farlo. Nel frattempo ho verificato alcune soluzioni che mi hanno fatto capire che il problema sta altrove.

Ho testato gli oggetti personalizzati WebResponse e WebRequest, libreria libCURL.NET e anche Sockets.

La differenza di tempo è stata la compressione gzip. La durata del flusso compresso era semplicemente la metà della normale durata del flusso e quindi il tempo di download era inferiore a 3 secondi con il browser.

ho messo un po 'di codice, se qualcuno vorrà sapere come ho risolto in questo modo: (alcune intestazioni non sono necessari)

public static string DownloadString(string URL) 
    { 
     WebClient client = new WebClient(); 
     client.Headers["User-Agent"] = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/532.5 (KHTML, like Gecko) Chrome/4.1.249.1045 Safari/532.5"; 
     client.Headers["Accept"] = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"; 
     client.Headers["Accept-Encoding"] = "gzip,deflate,sdch"; 
     client.Headers["Accept-Charset"] = "ISO-8859-2,utf-8;q=0.7,*;q=0.3"; 

     Stream inputStream = client.OpenRead(new Uri(URL)); 
     MemoryStream memoryStream = new MemoryStream(); 
     const int size = 32 * 4096; 
     byte[] buffer = new byte[size]; 

     if (client.ResponseHeaders["Content-Encoding"] == "gzip") 
     { 
      inputStream = new GZipStream(inputStream, CompressionMode.Decompress); 
     } 

     int count = 0; 
     do 
     { 
      count = inputStream.Read(buffer, 0, size); 
      if (count > 0) 
      { 
       memoryStream.Write(buffer, 0, count); 
      } 
     } 
     while (count > 0); 

     string result = Encoding.Default.GetString(memoryStream.ToArray()); 
     memoryStream.Close(); 
     inputStream.Close(); 
     return result; 
    } 

Penso che funzioni asyncro sarà quasi lo stesso. Ma userò semplicemente un altro thread per attivare questa funzione. Non ho bisogno dell'indicazione di progresso di percise.

+0

Che cosa esattamente non è così buono? Dov'è la stringa su cui scrivi? – Marcel

+0

Non è buono perché il risultato è solo 2048 (iSize) caratteri dalla fine del file. Ed è molto lento a scaricare tutto. – Kaminari

+0

Sembra che tu stia leggendo lo stesso 'Stream' in due posti; 'reader' e' streamRemote' ... Sono confuso ... Inoltre; attenzione: quando si gestiscono i byte non si leggono sempre i blocchi ('iByteSize') che sono numeri interi di caratteri. Inoltre non è molto chiaro come gli aggiornamenti dell'interfaccia utente si verificano nella richiamata; se questo implica un thread-switch che aggiungerà ritardi (così come il maledetto 'DoEvents'). –

risposta

1

Ricevi gli ultimi iSize byte dal tuo file poiché sovrascrivi il buffer su ogni iterazione, non stai salvando il buffer da nessuna parte. Ecco un esempio su come memorizzare il file in memoria utilizzando un MemoryStream.

var totalBytes = new MemoryStream(1024 * 1024); 
while ((iByteSize = streamRemote.Read(byteBuffer, 0, iByteSize)) > 0) 
{ 
    totalBytes.Write(byteBuffer, 0, iByteSize); 
    iRunningByteTotal += iByteSize; 

    //Some progress calculation 
    if (Progress != null) Progress(iProgressPercentage); 
} 

Quando l'intero download è completo, è possibile convertirlo in testo.

var byteArray = totalBytes.GetBuffer(); 
var numberOfBytes = totalBytes.Length; 
var text = Encoding.Default.GetString(byteArray, 0, numberOfBytes); 

Aggiornamento: il metodo DownloadStringAsync fa fondamentalmente lo stesso come sopra, ma non vi darà alcuna indicazione progresso. Esistono anche altri metodi asincroni che generano l'evento DownloadProgressChanged.

Aggiornamento 2: Riguardo al tempo di risposta. Hai scaricato a tempo la risorsa utilizzando qualche altro strumento? I principali browser hanno il supporto integrato per la tempistica di questo tipo.

Inoltre, si tratta di un file statico che serve o del contenuto generato sul lato server?

Una terza cosa che viene in mente è il buffering serveride. Per esempio. se viene utilizzata la proprietà Response.Buffer in ASP.Net, nulla verrà inviato al client fino a quando l'intero file/pagina non viene eseguito su serveride. Quindi il cliente dovrà aspettare prima di poter iniziare a scaricare.

+0

Il browser carica tutta la pagina in circa 5 secondi. Scarica la stringa La velocità asincrona è la stessa. Ma stranamente ho parecchi progressi 0 e 100 alla fine. Non è asincrono come penso che sarebbe. – Kaminari

+0

@Kaminari - di quanti file stiamo parlando qui? se sono piccoli, sarebbe probabilmente più efficiente servirti tutti in una volta, piuttosto che tagliare il download in piccoli pezzi. –

+0

@Peter Lillevold non è un file. È un sito web. Ma solo html senza altri elementi. Dimensioni circa 1,33 MB. – Kaminari

1

Sono molto confuso dalla doppia lettura, ma sembra come effettivamente intenzione di fare qualcosa di simile:

 StringBuilder sb = new StringBuilder();   
     using (StreamReader reader = new StreamReader(streamRemote)) 
     { 
      char[] charBuffer = new char[bufferSize]; 
      int charsRead; 
      while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0) 
      { 
       sb.Append(charBuffer, 0, charsRead); 
       //Some progress calculation 

       if (Progress != null) Progress(iProgressPercentage); 
      } 
     } 
     string result = sb.ToString(); 

vedere se funziona, se lo desideri .. Mi chiedo, tuttavia, se il Progress non è la causa della caduta; provalo senza questo assegnato, vedi se questo lo rende più veloce. Oppure eseguirlo periodicamente solo:

  //[snip] 
      int iteration = 0, charsRead; 
      while ((charsRead = reader.Read(charBuffer, 0, bufferSize)) > 0) 
      { 
       sb.Append(charBuffer, 0, charsRead); 
       //Some progress calculation 
       if((++iteration % 20) == 0 && Progress != null) { 
        Progress(iProgressPercentage); 
       } 
      } 
      //[snip] 

Inoltre, provare ad aumentare la dimensione del buffer.

+0

Ma perché aprire lo stream richiede la stessa quantità di tempo che il browser Web scarica per l'intera pagina? – Kaminari

Problemi correlati