C# 如何在.NET中下载大文件(通过HTTP)?
我需要在C#控制台应用程序中通过HTTP下载一个大文件(2GB)。问题是,大约1.2GB之后,应用程序的内存就会耗尽 以下是我使用的代码:C# 如何在.NET中下载大文件(通过HTTP)?,c#,.net,http,large-files,C#,.net,Http,Large Files,我需要在C#控制台应用程序中通过HTTP下载一个大文件(2GB)。问题是,大约1.2GB之后,应用程序的内存就会耗尽 以下是我使用的代码: WebClient request = new WebClient(); request.Credentials = new NetworkCredential(username, password); byte[] fileData = request.DownloadData(baseURL + fName); 正如你所看到的。。。我正在把文件直接读入
WebClient request = new WebClient();
request.Credentials = new NetworkCredential(username, password);
byte[] fileData = request.DownloadData(baseURL + fName);
正如你所看到的。。。我正在把文件直接读入内存。我很确定,如果我将数据从HTTP中分块读取并写入磁盘上的文件,我可以解决这个问题
我怎样才能做到这一点呢?您需要获取响应流,然后读取块,将每个块写入一个文件,以便重用内存 正如您所写的,整个响应都是2GB,需要存储在内存中。即使在64位系统上,单个.NET对象也会达到2GB限制
更新:更简单的选择。让
WebClient
为您完成这项工作:使用它的方法将数据直接放入文件。WebClient类是用于简化场景的类。一旦你通过了简单的场景(你已经做到了),你将不得不退一步使用WebRequest
有了WebRequest,您就可以访问响应流,并且可以在响应流上循环,读一点,写一点,直到完成
从Microsoft文档中:
我们不建议您将WebRequest或其派生类用于
新的发展。相反,使用类
资料来源:
例如:
public void MyDownloadFile(Uri url, string outputFilePath)
{
const int BUFFER_SIZE = 16 * 1024;
using (var outputFileStream = File.Create(outputFilePath, BUFFER_SIZE))
{
var req = WebRequest.Create(url);
using (var response = req.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
var buffer = new byte[BUFFER_SIZE];
int bytesRead;
do
{
bytesRead = responseStream.Read(buffer, 0, BUFFER_SIZE);
outputFileStream.Write(buffer, 0, bytesRead);
} while (bytesRead > 0);
}
}
}
}
请注意,如果WebClient.DownloadFile有效,那么我认为它是最好的解决方案。在“下载文件”的答案发布之前,我写了上述内容。我也在早上写得太早了,所以可能需要一点盐(和测试)。如果使用,可以直接保存到文件中。我会使用类似于的WebClient.OpenRead返回一个流,只需使用Read循环内容,因此,数据不会缓冲在内存中,而是可以分块写入文件。连接可能会中断,因此最好分块下载文件 Akka streams可以使用多线程技术帮助从System.IO.Stream下载小块文件 下载方法将把字节附加到以long fileStart开头的文件中。如果文件不存在,fileStart值必须为0
using Akka.Actor;
using Akka.IO;
using Akka.Streams;
using Akka.Streams.Dsl;
using Akka.Streams.IO;
private static Sink<ByteString, Task<IOResult>> FileSink(string filename)
{
return Flow.Create<ByteString>()
.ToMaterialized(FileIO.ToFile(new FileInfo(filename), FileMode.Append), Keep.Right);
}
private async Task Download(string path, Uri uri, long fileStart)
{
using (var system = ActorSystem.Create("system"))
using (var materializer = system.Materializer())
{
HttpWebRequest request = WebRequest.Create(uri) as HttpWebRequest;
request.AddRange(fileStart);
using (WebResponse response = request.GetResponse())
{
Stream stream = response.GetResponseStream();
await StreamConverters.FromInputStream(() => stream, chunkSize: 1024)
.RunWith(FileSink(path), materializer);
}
}
}
使用Akka.Actor;
使用Akka.IO;
使用Akka.Streams;
使用Akka.Streams.Dsl;
使用Akka.Streams.IO;
专用静态接收器文件链接(字符串文件名)
{
返回流。创建()
.ToMaterialized(FileIO.ToFile(新的FileInfo(filename)、FileMode.Append)、Keep.Right);
}
私有异步任务下载(字符串路径、Uri、长文件启动)
{
使用(var system=ActorSystem.Create(“系统”))
使用(var materializer=system.materializer())
{
HttpWebRequest-request=WebRequest.Create(uri)为HttpWebRequest;
AddRange(fileStart);
使用(WebResponse=request.GetResponse())
{
Stream=response.GetResponseStream();
等待StreamConverters.FromInputStream(()=>stream,chunkSize:1024)
.RunWith(文件链接(路径),物化器);
}
}
}
Genius。这最终实现了一个梦想。谢谢你的帮助!仅供参考。单元测试像WebClient这样不实现任何接口的类可能是一项挑战。您能否提供示例代码,说明如何使用WebClient.DownloadFile直接保存到文件?感谢您的详细回答和代码片段!当我想要在数据到达时处理数据时,这将非常有用!这段代码中的异常处理或重试机制如何?网络断开等。在大多数情况下,最好的异常处理是完全没有异常。如果您的网络非常不可靠,则可能需要添加重试逻辑。我住在美国,所以我想我通常被良好的网络连接宠坏了。当它们不起作用时,情况非常糟糕,重试不是一个有用的选项。我想知道为什么您选择了16*1024的缓冲区大小。当我试图增加大小时,它似乎仍然使用较小的块。你的选择背后有什么道理吗。只是好奇而已。是否可以在这里使用HttpClient而不是WebRequest