我可以在不经过内存的情况下直接从HttpResponseMessage流式传输到文件吗
本文关键字:HttpResponseMessage 传输 文件 经过 内存 情况下 我可以 | 更新日期: 2023-09-27 18:03:00
我的程序使用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
,这样只有一小部分流保存在内存中。
我们计划在大文件(>1GB(上使用它,那么有没有一种方法可以在不将所有文件都存储在内存中的情况下实现这一点?
理想情况下,不手动循环读取byte[]
的一部分并将该部分写入文件流,直到写入所有内容?
看起来这是经过设计的——如果你检查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
,就像在原始示例中一样。
使用具有HttpCompletionOption.ResponseHeadersRead
值的GetAsync(Uri, HttpCompletionOption)
重载,而不是GetAsync(Uri)
。
这同样适用于SendAsync
和HttpClient
的其他方法
来源:
- 文档(见备注(https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.getasync?view=netcore-1.1#System_Net_Http_HttpClient_GetAsync_System_Uri_System_Net_Http_HttpCompletionOption_
读取部分或所有响应(包括内容(后,返回的Task对象将基于completionOption参数完成。
-
使用
HttpCompletionOption.ResponseHeadersRead
的GetStreamAsync
的.NET核心实现https://github.com/dotnet/corefx/blob/release/1.1.0/src/System.Net.Http/src/System/Net/Http/HttpClient.cs#L163-L168 -
具有大响应的HttpClient内存使用率峰值
- 是否使用自定义请求的HttpClient.GetStreamAsync((?(不要介意评论回应,
ResponseHeadersRead
才是关键(
另一种简单快捷的方法是:
public async Task<bool> DownloadFile(string url)
{
using (MemoryStream ms = new MemoryStream()) {
new HttpClient().GetStreamAsync(webPath).Result.CopyTo(ms);
... // use ms in what you want
}
}
现在您已经在ms中下载了作为流的文件。