我可以直接从HttpResponseMessage流式传输到文件而无需通过内存吗?

Seb*_*ter 8 c# stream asp.net-web-api2

我的程序使用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;
}
Run Code Online (Sandbox Code Playgroud)

现在,当此代码运行时,该进程会将所有文件加载到内存中.我实际上宁愿期望流从HttpResponseMessage.Content流式传输到FileStream,因此只有一小部分流被保存在内存中.

我们计划在大文件(> 1GB)上使用它,那么有没有办法在没有内存中的所有文件的情况下实现它?

理想情况下,没有手动循环读取一部分到byte []并将该部分写入文件流直到所有内容都被写入?

Art*_*964 9

而不是GetAsync(Uri)使用GetAsync(Uri, HttpCompletionOption)带有HttpCompletionOption.ResponseHeadersRead值的重载。

这同样适用于SendAsync和其他方法HttpClient

资料来源:

读取部分或全部响应(包括内容)后,返回的Task对象将根据completionOption参数完成。


Iri*_*ium 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
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果请求返回不成功的响应代码,则会抛出异常,因此您可能希望在原始示例中包装try..catch并返回false此情况.