【问题标题】:Gzip MultipartFormDataContent Time Out When Posted To The ServerGzip MultipartFormDataContent 发布到服务器时超时
【发布时间】:2020-08-06 19:36:15
【问题描述】:

我正在尝试在真实案例场景中发布 MultipartFormDataContent,数据内容对象可以包含从简单字符串到视频文件的任何内容,我在下面使用序列化对象,只是一个概念证明。 另外我想指出,使用 JSON 对象不会满足我的现实生活场景

 public class GzipMultipartContent : MultipartFormDataContent
 {
    public GzipMultipartContent()
    {
       Headers.ContentEncoding.Add("gzip");
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        return Task.Factory.StartNew(() =>
        {
            using (var gzip = new GZipStream(stream, CompressionMode.Compress, true))
                base.SerializeToStreamAsync(gzip, context);
        });
    }
}

我是这样称呼它的

var gzipped = new GzipMultipartContent();
var test = new TestClass();
gzipped.Add(new StringContent(JsonConvert.SerializeObject(test)), "value");
var client = new HttpClient();
var result = client.PostAsync("http://localhost:60001/api/Home/", gzipped).Result;

这是控制器中的发布操作

// POST: api/Home
[HttpPost]
public void Post([FromForm] object value)
{

}

我在服务器端添加了一个断点并确保它甚至没有到达 Post 方法,我还尝试了一个正常的 POST 请求以确保它不是服务器配置问题或 URL 输入错误

【问题讨论】:

    标签: c# .net http gzip


    【解决方案1】:

    我不确定我是否理解了这个问题,但如果是您的请求没有到达服务器,而您的“正常”POST 请求是,那么我认为我找到了您的问题。

    我认为问题在于您的服务器不知道它的 Content-Type 是什么。我从字面上复制粘贴了您的代码,但添加了

    Headers.ContentType = new MediaTypeHeaderValue("application/x-gzip"); 
    

    GzipMultipartContent.csctor。

    添加类型后,我在 localhost 服务器中命中了断点。

    来源:Content-Type

    在请求中(例如 POST 或 PUT),客户端告诉服务器实际发送的是什么类型的数据。

    【讨论】:

      【解决方案2】:

      客户端

      如果有问题的代码是你的真实代码,那么至少有两个问题:

      没有等待base.SerializeToStreamAsync

      您创建了一个新任务,但您没有等到基类完成写入任务中的压缩流。因此,您可以将不可预测的内容发送到服务器。

      没有覆盖Content-Length

      MultipartFormDataContent根据未压缩的数据计算内容的长度,因为你有压缩数据,你必须重新计算压缩数据的长度。

      坦率地说,我认为您不需要继承 MultipartFormDataContent 来使其压缩。相反,您可以将整个 MultipartFormDataContent 压缩到包装器 HttpContent 中:

      public class GzipCompressedContent : HttpContent
      {
          private readonly HttpContent _content;
          
          public GzipCompressedContent(HttpContent content)
          {
              // Copy original headers
              foreach (KeyValuePair<string, IEnumerable<string>> header in content.Headers)
              {
                  Headers.TryAddWithoutValidation(header.Key, header.Value);
              }
      
              Headers.ContentEncoding.Add("gzip");        
              _content = content;     
          }
          
          protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
          {
              using (var gzip = new GZipStream(stream, CompressionMode.Compress, true))
              {
                  // Compress the entire original content
                  await _content.CopyToAsync(gzip);
              }       
          }
      
          protected override bool TryComputeLength(out long length)
          {
              // Content-Lenght is optional, so set to -1
              length = -1;
              return false;
          }
      }
      

      并使用它:

      var test = new TestClass();
      
      using (var client = new HttpClient())
      {
          var form = new MultipartFormDataContent();
          form.Add(new StringContent(JsonConvert.SerializeObject(test)), "value");
      
          var compressed = new GzipCompressedContent(form);
      
          var result = await client.PostAsync(..., compressed);
      }
      

      服务器端

      您的服务器需要支持压缩流。

      例如,默认情况下,ASP.NET Core 不支持压缩请求,如果你向 ASP.NET Core 应用程序发送 GZip 压缩请求,你会看到异常:

      System.IO.IOException: Unexpected end of Stream, the content may have already been read by another component. 
         at Microsoft.AspNetCore.WebUtilities.MultipartReaderStream.ReadAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
      

      上述异常发生在任何控制器动作发生之前的动作调用管道中。因此无法达到这种情况下的控制器操作。

      要解决此问题,您需要启用服务器端请求解压缩支持。

      如果您使用的是 ASP.NET Core,请查看this nuget package

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-03
        • 1970-01-01
        • 2011-08-17
        • 1970-01-01
        • 2017-07-04
        • 2016-01-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多