我的程序使用HttpClient向Web API发送GET请求,并返回一个文件。
我现在使用此代码(简化)将文件存储到光盘:
public async Task<bool> DownloadFile()
{
var client = new HttpClient();
var uri = new Uri("http://somedomain.com/path");
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var fileName = response.Content.Headers.ContentDisposition.FileName;
using (var fs = new FileStream("C:\test\" + fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
await response.Content.CopyToAsync(fs);
return true;
}
}
return false;
}
现在,当此代码运行时,该进程会将所有文件加载到内存中。我实际上宁愿期望流从HttpResponseMessage.Content流式传输到FileStream,因此只有一小部分流被保存在内存中。
我们计划在大文件(&gt; 1GB)上使用它,所以有没有办法在没有内存中的所有文件的情况下实现这一目标?
理想情况下,无需手动循环读取一部分到byte []并将该部分写入文件流,直到所有内容都被写入?
答案 0 :(得分:7)
看起来这是设计 - 如果你查看HttpClient.GetAsync()
的文档,你会看到它说:
返回的任务对象将在整个响应后完成 (包括内容)已读取
您可以使用明确指出的HttpClient.GetStreamAsync()
:
此方法不会缓冲流。
但是你不然后就我所见,可以访问响应中的标题。因为这可能是一个要求(当你从头文件中获取文件名时),那么你可能想要使用HttpWebRequest
代替它,这样你就可以获得响应细节(标题等),而无需阅读整个响应进入记忆。类似的东西:
public async Task<bool> DownloadFile()
{
var uri = new Uri("http://somedomain.com/path");
var request = WebRequest.CreateHttp(uri);
var response = await request.GetResponseAsync();
ContentDispositionHeaderValue contentDisposition;
var fileName = ContentDispositionHeaderValue.TryParse(response.Headers["Content-Disposition"], out contentDisposition)
? contentDisposition.FileName
: "noname.dat";
using (var fs = new FileStream(@"C:\test\" + fileName, FileMode.Create, FileAccess.Write, FileShare.None))
{
await response.GetResponseStream().CopyToAsync(fs);
}
return true
}
请注意,如果请求返回不成功的响应代码,则会抛出异常,因此您可能希望在try..catch
中包含并返回false
,就像在原始示例中一样。
答案 1 :(得分:0)
将GetAsync(Uri)
重载与GetAsync(Uri, HttpCompletionOption)
一起使用,而不是HttpCompletionOption.ResponseHeadersRead
。
SendAsync
和HttpClient
的其他方法也是如此
来源:
读取部分或所有响应(包括内容)后,返回的Task对象将基于completionOption参数完成。
使用GetStreamAsync
https://github.com/dotnet/corefx/blob/release/1.1.0/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L163-L168
HttpCompletionOption.ResponseHeadersRead
的.NET Core实现
ResponseHeadersRead
是诀窍)