c# - HttpClient 在下载时不写入流

标签 c# http stream win-universal-app dotnet-httpclient

目前,我正在实现一种使用 HttpClient 报告进度的方法,因为我们与 .NET4 WPF 和 Windows 通用应用程序共享代码,所以我们使用 NuGet 中的 Microsoft HTTP 客户端库。这个想法是将目标文件流包装在 CountingInputStream 中并在那里报告进度:

 public override void Write(byte[] buffer, int offset, int count)
    {
        _stream.Write(buffer, offset, count);

        _bytesRead += count;
        _progress.Report(_bytesRead);

        if (_cancellationToken.IsCancellationRequested)
        {
            _cancellationToken.ThrowIfCancellationRequested();
        }
    }

然后我发送请求: HttpResponseMessage httpResponseMessage = AsyncHelpers.RunSync(() => _httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, CancelToken));

之后,我打开文件流,然后复制内容流。响应具有正确的 header : 内容长度:213334 内容类型:应用程序/八位字节流;字符集=UTF-8 内容处置:附件; filename="邦迪海滩.jpg";文件名*=UTF-8''Bondi%20Beach.jpg

using(Stream fileStream = new CountingInputStream(storage.Open(downloadRequest.TargetPath, FileMode.Create), downloadRequest.Progress, cancellationToken )) {                                               
                await HttpHeaderResponseMessage.Content.CopyToAsync(fileStream);
       }

问题是 StreamContent 仅在下载完成后才开始写入文件流。当它开始编写进度报告时,效果就很好。

我已经尝试过不同的方法,例如:

  • ReadAsStreamAsync 然后将响应流复制到文件流
  • ReadAsStreamAsync手动读取缓冲区然后写入文件流
  • _httpClient = new System.Net.Http.HttpClient(){MaxResponseContentBufferSize = 4096};限制 BufferSize

有什么想法可以强制 ContentStream 在下载时写入文件流吗?

更新: 根据 Luaans 的建议,我尝试重写 WriteAsync 并实现 StreamContent Extensions 方法:

//CountingInputStream
public Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        _bytesRead += count;
        _progress.Report(_bytesRead);

        if (_cancellationToken.IsCancellationRequested)
        {
            _cancellationToken.ThrowIfCancellationRequested();
        }
        return _stream.WriteAsync(buffer, offset, count, cancellationToken);
    }
//static Extensions Class
public static async Task CopyToAs(this StreamContent source, Stream targetStream)
    {
        int read;
        byte[] buffer = new byte[4096];
        using(Stream responseStream = await source.ReadAsStreamAsync()) {
            while ((read = await responseStream.ReadAsync(buffer,0,buffer.Length))>0) {
                await targetStream.WriteAsync(buffer, 0, read);
            }
        }
    }

它仍然会等待下载完成,直到第一次调用ReadAsync。有什么提示我做错了什么吗?

最佳答案

事实上,ReadAsStreamAsync异步,这使得这一点相当可疑。为什么要异步等待获取?您应该异步读取它,但您应该立即准备好流本身。

阅读文档可以让这一点变得显而易见:

This operation will not block. The returned task object will complete after the whole response (including content) is read.

但是,您可以使用重载使其在读取 header 后返回。这仍然意味着您需要等待服务器处理请求,然后才能开始获取进度,但对于下载本身来说,您很幸运。

示例代码:

var response = 
 await 
 (
   new HttpClient()
   .GetAsync("http://www.microsoft.com/", HttpCompletionOption.ResponseHeadersRead)
 );

var stream = await response.Content.ReadAsStreamAsync();
var buffer = new byte[2048];    

while (await stream.ReadAsync(buffer, 0, buffer.Length) > 0)
{
  // Report progress and write to a different stream
}

编辑:

听起来您应该使用 Windows.Web.Http.HttpClient 而不是 System.Net.Http.HttpClient:

async Task DownloadWithProgress()
{
 var awaitable = httpClient.GetAsync(yourUrl)

 awaitable.Progress = (res, progress) =>
 {
   // Report progress
 }

 await awaitable;   
} 

关于c# - HttpClient 在下载时不写入流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29015127/

相关文章:

http - 如何让 paster serve 同时为 HTTP 和 HTTPS 请求提供服务?

angular - 访问被 CORS 策略 : Response to preflight request doesn't pass access control check 阻止

c# - 如何将 textbox.text 转换为 int 或仅用于 int 输入的内容?

c# - 在 C# 中计算与 Regex 的重叠匹配

c - 套接字事务未通过

JAVA流: re-use double a DoubleStream

flutter - 如何从 firestore 获取实时更新,同时保持构建方法的纯净?

c++ - C++检测输入是否不满足条件

c# - 与流程一起打印输出

c# - UWP:ListView ItemClick 不起作用