【问题标题】:How to compress http request on the fly and without loading compressed buffer in memory如何即时压缩http请求而不在内存中加载压缩缓冲区
【发布时间】:2013-05-21 15:25:29
【问题描述】:

我需要在 http post 请求中将大量数据发送到支持 gzip 编码请求的服务器。

从简单开始

public async Task<string> DoPost(HttpContent content)
{
  HttpClient client = new HttpClient();
  HttpResponseMessage response = await client.PostAsync("http://myUri", content);

  response.EnsureSuccessStatusCode();
  return await response.Content.ReadAsStringAsync();
}

我刚刚添加了预压缩

public async Task<string> DoPost(HttpContent content, bool compress)
{
  if (compress) 
    content= await CompressAsync(content);

  return await DoPost(content);
}

private static async Task<StreamContent> CompressAsync(HttpContent content)
{
  MemoryStream ms = new MemoryStream();
  using (GZipStream gzipStream = new GZipStream(ms, CompressionMode.Compress, true))
  {
    await content.CopyToAsync(gzipStream);
    await gzipStream.FlushAsync();
  }

  ms.Position = 0;
  StreamContent compressedStreamContent = new StreamContent(ms);
  compressedStreamContent.Headers.ContentType = content.Headers.ContentType;
  compressedStreamContent.Headers.Add("Content-Encoding", "gzip");

  return compressedStreamContent;
}

它运行良好,但压缩数据在发送请求之前已完全加载到内存中。我希望能够在以流方式发送期间动态压缩数据。

为此,我尝试了以下代码:

private static async Task<HttpContent> CompressAsync2(HttpContent content)
{
  PushStreamContent pushStreamContent = new PushStreamContent(async (stream, content2, transport) =>
  {
    using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, true))
    {
      try
      {
        await content.CopyToAsync(gzipStream);
        await gzipStream.FlushAsync();
      }
      catch (Exception exception)
      {
        throw;
      }
    }
  });
  pushStreamContent.Headers.ContentType = content.Headers.ContentType;
  pushStreamContent.Headers.Add("Content-Encoding", "gzip");

  return pushStreamContent;
}

但它永远不会超出 CopyToAsync(gzipStream)。 FlushAsync 永远不会执行,也不会引发异常,Fiddler 也看不到任何帖子开始。

我的问题是:

  • 为什么 CompressAsync2 不起作用?
  • 如何在发送过程中即时压缩而不将压缩缓冲区加载到内存中?

任何帮助将不胜感激。

【问题讨论】:

  • PushStreamContent 不(当前)支持 async lambdas。
  • @Stephen Cleary:你说得对,我应该检查一下!而且我不能从 PushStreamContent 派生来重载 SerializeToStreamAsync(仅限内部)。你能找到解决办法吗?
  • 嗯,最简单的方法是使用PushStreamContent and modify it 来支持async lambdas。它已经在我的“待办事项”清单上几个月了,只是还没开始。

标签: .net c#-4.0 compression gzip dotnet-httpclient


【解决方案1】:

尝试使用 WebAPIContrib https://github.com/WebApiContrib/WebAPIContrib/blob/master/src/WebApiContrib/Content/CompressedContent.cs 中的 CompressedContent 类

public async Task<string> DoPost(HttpContent content)
{
  HttpClient client = new HttpClient();
  HttpResponseMessage response = await client.PostAsync("http://myUri", 
                                 new CompressedContent(content,"gzip"));

  response.EnsureSuccessStatusCode();
  return await response.Content.ReadAsStringAsync();
}

附:这只会在 .net 4.5 上流式传输内容。 .net 4 版本的 HttpWebRequest 总是缓冲发送的内容。

P.P.S.为每个请求创建一个新的 HttpClient 并不是使用 HttpClient 的最佳方式。这样做将强制为每个请求创建一个新的 TCP 连接。

【讨论】:

  • 真正有趣的链接:非常感谢。我需要继续使用 .Net 4.0(需要 XP)。即使我为 .Net 4.0 进行编译,您认为安装 .Net 4.5 后我可以进行流式传输吗?
  • 我创建了一个本地 HttpClient 只是为了问题简单。
  • @MuiBienCarlota 我不确定。我的猜测是肯定的,但你需要测试。关于 HttpClient... 很酷,我只是经常看到它,我的任务是消灭它 ;-)
  • 我有答案:不是。当为 .Net 4.0 编译时,我们不是流式传输方式(即使存在 .Net 4.5)。我将在 .Net 4.5 下尝试。
  • 它在以 .Net 4.5 为目标时有效,在以 .Net 4.0 为目标时无效,即使存在 .Net 4.5。让我吃惊。我在想.Net 4.5 是.Net 4.0 的完全替代品,即如果安装了 4.5,我的目标 4.0 的程序应该在 4.5 下运行。神秘!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-28
  • 2023-03-06
  • 1970-01-01
  • 2011-05-31
  • 2010-09-18
  • 1970-01-01
相关资源
最近更新 更多