尝试下载大文件时OutOfMemoryException

时间:2018-03-05 08:55:04

标签: c#

我有以下代码:

byte[] fileBytes = null;

using (var wc = new System.Net.WebClient())
{
  fileBytes = wc.DownloadData(filename);
}

filename是一个包含“file://example.com/Media-Data/thisisabigmovie.wmv”之类的网址

现在我的客户似乎有很大的文件会引发以下错误:

System.OutOfMemoryException: 
   at System.Net.ScatterGatherBuffers.AllocateMemoryChunk(Int32 newSize)
   at System.Net.ScatterGatherBuffers.Write(Byte[] buffer, Int32 offset, Int32 count)
   at System.Net.WebClient.DownloadBitsState.RetrieveBytes(Int32& bytesRetrieved)
   at System.Net.WebClient.DownloadBits(WebRequest request, Stream writeStream, CompletionDelegate completionDelegate, AsyncOperation asyncOp)
   at System.Net.WebClient.DownloadDataInternal(Uri address, WebRequest& request)
   at System.Net.WebClient.DownloadData(Uri address)
   at System.Net.WebClient.DownloadData(String address)

有没有办法解决这个问题?也许以“chunks”下载文件?

2 个答案:

答案 0 :(得分:7)

不要使用DownloadData,因为它会在Byte[]中返回响应,该响应将达到.NET的2GB最大对象大小限制。

您应该使用HttpClient或原始HttpWebRequest对象而不是WebClient,并将响应流直接传输到磁盘上的FileStream左右。使用CopyToAsync方法:

using( Stream responseStream = response.GetResponseStream() )
using( FileStream fileStream = new FileStream( "tempFile.bin" ) )
{
    await responseStream.CopyToAsync( fileStream ).ConfigureAwait(false);
}  

答案 1 :(得分:2)

您打算在下载后对该文件执行什么操作?如果您立即将其保存到磁盘,则只需使用WebClient的DownloadFile方法而不是DownloadData。正如您猜测的那样,DownloadFile在内部使用FileStream并以块的形式保存到磁盘:

using (var wc = new System.Net.WebClient())
{
   wc.DownloadFile(remoteUri, localFilename);
}

如果您需要处理字节而不仅仅是保存它们,您应该使用基于流的方法将数据作为流来获取 - 这意味着您在它到达时处理它,而不是在您将数据全部存储之后在记忆中。为此,WebClient不会帮助您,您应该使用较新的HttpClient或较旧的,更低级别的HttpWebRequest

var response = await new HttpClient().GetAsync(remoteUrl);
var dataStream = await response.Content.ReadAsStreamAsync();

或     var request =(HttpWebRequest)WebRequest.Create(remoteUrl);     var response = request.GetResponse();     var dataStream = response.GetResponseStream();