C# 如何在.NET中下载大文件(通过HTTP)?

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); 正如你所看到的。。。我正在把文件直接读入

我需要在C#控制台应用程序中通过HTTP下载一个大文件(2GB)。问题是,大约1.2GB之后,应用程序的内存就会耗尽

以下是我使用的代码:

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