2010-04-26 13 views
6

Me gustaría utilizar el WebClient (¿o hay otra opción mejor?) Pero hay un problema. Entiendo que abrir el flujo lleva algo de tiempo y esto no se puede evitar. Sin embargo, leerlo toma una cantidad de tiempo mucho más extraña en comparación con leerlo completamente de inmediato.¿Cómo descargar un archivo en una cadena con una devolución de llamada de progreso?

¿Existe alguna forma de hacerlo? Me refiero a dos formas, a la cadena y al archivo. Progress es mi propio delegado y está funcionando bien.


ACTUALIZACIÓN QUINTO:

Finalmente, logró hacerlo. Mientras tanto, verifiqué algunas soluciones que me hicieron darme cuenta de que el problema está en otra parte.

He probado objetos personalizados WebResponse y WebRequest, biblioteca libCURL.NET e incluso Sockets.

La diferencia en el tiempo fue la compresión gzip. La duración de la secuencia comprimida era simplemente la mitad de la duración normal de la transmisión y, por lo tanto, el tiempo de descarga era inferior a 3 segundos con el navegador.

que poner un poco de código si alguien va a querer saber cómo resolví esto: (no se necesitan algunas cabeceras)

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; 
    } 

Creo que funciona asyncro serán casi los mismos. Pero simplemente usaré otro hilo para activar esta función. No necesito una indicación de progreso precisa.

+0

¿Qué exactamente no es tan bueno? ¿Dónde está la cuerda a la que escribes? – Marcel

+0

No es bueno porque el resultado es solo 2048 caracteres (iSize) del final del archivo. Y es muy lento para descargar todo. – Kaminari

+0

Parece que estás leyendo ese mismo 'Stream' en dos lugares; 'reader' y' streamRemote' ... Estoy confundido ... También; cuidado: cuando maneja bytes, no siempre lee bloques ('iByteSize') que son números enteros de caracteres. Tampoco está muy claro cómo las actualizaciones de UI suceden en la devolución de llamada; si esto implica un cambio de hilo que agregará retrasos (como lo hará el maldito 'DoEvents'). –

Respuesta

1

Solo obtiene los últimos iSize bytes de su archivo, ya que sobrescribe el búfer en cada iteración, no está guardando el búfer en ninguna parte. Aquí hay un ejemplo de cómo almacenar el archivo en la memoria usando 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); 
} 

Cuando termine la descarga completa, puede convertirla en texto.

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

Actualización: el método DownloadStringAsync básicamente hace lo mismo que el anterior, pero no le dará ninguna indicación de progreso. Sin embargo, existen algunos otros métodos asíncronos que activarán el evento DownloadProgressChanged.

Actualización 2: En relación con el tiempo de respuesta. ¿Ha programado la descarga del recurso usando alguna otra herramienta? Los principales navegadores tienen soporte integrado para cronometrar tales tings.

Además, ¿es un archivo estático su publicación o el contenido generado en el servidor?

Una tercera cosa que viene a la mente es el almacenamiento en la memoria del servidor. P.ej. si se utiliza la propiedad Response.Buffer en ASP.Net, no se enviará nada al cliente hasta que todo el archivo/página se haya terminado en el servidor. Por lo tanto, el cliente tendrá que esperar antes de que pueda comenzar la descarga.

+0

El navegador carga toda la página en aproximadamente 5 segundos. Descargar String Async speed es lo mismo. Pero estranguladamente obtengo varios 0 de progreso y 100 al final. No es tan sincronizado como creo que sería. – Kaminari

+0

@Kaminari: ¿de qué tamaño de archivos estamos hablando aquí? si son pequeños, probablemente sea más eficiente atenderlos a todos de una vez, en lugar de cortar la descarga en pedazos pequeños. –

+0

@Peter Lillevold no es un archivo. Es un sitio web Pero solo html sin otros elementos. Tamaño alrededor de 1,33MB. – Kaminari

1

Estoy muy confundido por la doble lectura, pero se ve como si realmente la intención de hacer algo como:

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

ver si funciona como se desea .. Me pregunto, sin embargo, si el Progress no es la causa de la caída; Pruébalo sin esto asignado, mira si eso lo hace más rápido. O ejecute esto periódicamente:

  //[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] 

Además, intente aumentar el tamaño del búfer.

+0

¿Pero por qué abrir la transmisión lleva el mismo tiempo que el navegador web para descargar toda la página? – Kaminari

Cuestiones relacionadas