metodo example create c# file web download webrequest

c# - example - WebRequest no puede descargar archivos grandes(~ 1 GB) correctamente



post web api c# (4)

Estoy intentando descargar un archivo grande desde una URL pública. Parecía funcionar bien al principio, pero 1/10 de las computadoras parecen agotarse. Mi primer intento fue usar WebClient.DownloadFileAsync pero como nunca se completaría, WebRequest.Create al uso de WebRequest.Create y leí los flujos de respuesta directamente.

Mi primera versión de usar WebRequest.Create encontró el mismo problema que WebClient.DownloadFileAsync . La operación se agota y el archivo no se completa.

Mi próxima versión agregó reintentos si se agota el tiempo de descarga. Aquí es donde se pone raro. La descarga finalmente termina con 1 reintento para terminar los últimos 7092 bytes. Por lo tanto, el archivo se descarga con exactamente el mismo tamaño, PERO el archivo está dañado y difiere del archivo de origen. Ahora espero que la corrupción esté en los últimos 7092 bytes, pero este no es el caso.

Usando BeyondCompare, he encontrado que faltan 2 trozos de bytes del archivo corrupto que totalizan hasta los 7092 bytes faltantes. Estos bytes faltantes están en 1CA49FF0 y 1E31F380 , mucho antes de que se agote el tiempo de descarga y se reinicie.

¿Qué podría estar pasando aquí? ¿Alguna pista sobre cómo rastrear más este problema?

Aquí está el código en cuestión.

public void DownloadFile(string sourceUri, string destinationPath) { //roughly based on: http://stackoverflow.com/questions/2269607/how-to-programmatically-download-a-large-file-in-c-sharp //not using WebClient.DownloadFileAsync as it seems to stall out on large files rarely for unknown reasons. using (var fileStream = File.Open(destinationPath, FileMode.Create, FileAccess.Write, FileShare.Read)) { long totalBytesToReceive = 0; long totalBytesReceived = 0; int attemptCount = 0; bool isFinished = false; while (!isFinished) { attemptCount += 1; if (attemptCount > 10) { throw new InvalidOperationException("Too many attempts to download. Aborting."); } try { var request = (HttpWebRequest)WebRequest.Create(sourceUri); request.Proxy = null;//http://stackoverflow.com/questions/754333/why-is-this-webrequest-code-slow/935728#935728 _log.AddInformation("Request #{0}.", attemptCount); //continue downloading from last attempt. if (totalBytesReceived != 0) { _log.AddInformation("Request resuming with range: {0} , {1}", totalBytesReceived, totalBytesToReceive); request.AddRange(totalBytesReceived, totalBytesToReceive); } using (var response = request.GetResponse()) { _log.AddInformation("Received response. ContentLength={0} , ContentType={1}", response.ContentLength, response.ContentType); if (totalBytesToReceive == 0) { totalBytesToReceive = response.ContentLength; } using (var responseStream = response.GetResponseStream()) { _log.AddInformation("Beginning read of response stream."); var buffer = new byte[4096]; int bytesRead = responseStream.Read(buffer, 0, buffer.Length); while (bytesRead > 0) { fileStream.Write(buffer, 0, bytesRead); totalBytesReceived += bytesRead; bytesRead = responseStream.Read(buffer, 0, buffer.Length); } _log.AddInformation("Finished read of response stream."); } } _log.AddInformation("Finished downloading file."); isFinished = true; } catch (Exception ex) { _log.AddInformation("Response raised exception ({0}). {1}", ex.GetType(), ex.Message); } } } }

Aquí está la salida del registro de la descarga corrupta:

Request #1. Received response. ContentLength=939302925 , ContentType=application/zip Beginning read of response stream. Response raised exception (System.Net.WebException). The operation has timed out. Request #2. Request resuming with range: 939295833 , 939302925 Received response. ContentLength=7092 , ContentType=application/zip Beginning read of response stream. Finished read of response stream. Finished downloading file.


Asigne un tamaño de búfer más grande que el tamaño de archivo esperado.

byte [] byteBuffer = new byte [65536];

de modo que, si el archivo tiene un tamaño de 1GiB, asigna un búfer de 1 GiB y luego intenta llenar todo el búfer en una llamada. Este relleno puede devolver menos bytes pero aún le ha asignado el búfer completo. Tenga en cuenta que la longitud máxima de una única matriz en .NET es un número de 32 bits, lo que significa que incluso si recompila su programa a 64 bits y en realidad tiene suficiente memoria disponible.


Debe cambiar la configuración del tiempo de espera. Parece que hay dos posibles problemas de tiempo de espera:

  • Tiempo de espera del lado del cliente: intente cambiar los tiempos de espera en WebClient. Encuentro que para descargas de archivos grandes a veces necesito hacer eso.
  • Tiempo de espera del lado del servidor: intente cambiar el tiempo de espera en el servidor. Puede validar si este es el problema usando otro cliente, por ejemplo, PostMan

Este es el método que suelo usar, no me ha fallado hasta ahora para el mismo tipo de carga que necesita. Intenta usar mi código para cambiar el tuyo un poco y ver si eso ayuda.

if (!Directory.Exists(localFolder)) { Directory.CreateDirectory(localFolder); } try { HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(Path.Combine(uri, filename)); httpRequest.Method = "GET"; // if the URI doesn''t exist, exception gets thrown here... using (HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse()) { using (Stream responseStream = httpResponse.GetResponseStream()) { using (FileStream localFileStream = new FileStream(Path.Combine(localFolder, filename), FileMode.Create)) { var buffer = new byte[4096]; long totalBytesRead = 0; int bytesRead; while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0) { totalBytesRead += bytesRead; localFileStream.Write(buffer, 0, bytesRead); } } } } } catch (Exception ex) { throw; }


Para mí, su método sobre cómo leer el archivo mediante el almacenamiento en búfer parece muy extraño. Tal vez el problema es que lo haces.

while(bytesRead > 0)

¿Qué pasa si, por alguna razón, el flujo no devuelve ningún byte en algún momento pero todavía no se ha terminado de descargar, entonces saldría del bucle y nunca regresaría? Debería obtener Content-Length e incrementar una variable totalBytesReceived by bytesRead. Finalmente cambias el bucle a

while(totalBytesReceived < ContentLength)