从 HTTPClient 响应中解压 GZip 流

Posted

技术标签:

【中文标题】从 HTTPClient 响应中解压 GZip 流【英文标题】:Decompressing GZip Stream from HTTPClient Response 【发布时间】:2021-10-29 23:17:50 【问题描述】:

我正在尝试连接到从 WCF 服务(WCF 服务到 WCF 服务)返回 GZip 编码 JSON 的 api。我正在使用 HTTPClient 连接到 API,并且能够将 JSON 对象作为字符串返回。但是我需要能够将返回的数据存储在数据库中,因此我认为最好的方法是将 JSON 对象返回并存储在数组或字节或类似的东西中。

我遇到的具体问题是 GZip 编码的解压缩,并且一直在尝试许多不同的示例,但仍然无法获得它。

以下代码是我如何建立连接并获得响应,这是从 API 返回字符串的代码。

public string getData(string foo)

    string url = "";
    HttpClient client = new HttpClient();
    HttpResponseMessage response;
    string responseJsonContent;
    try
    
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        response = client.GetAsync(url + foo).Result;
        responseJsonContent = response.Content.ReadAsStringAsync().Result;
        return responseJsonContent;
    
    catch (Exception ex)
    
        System.Windows.Forms.MessageBox.Show(ex.Message);
        return "";
    

我一直在关注一些不同的示例,例如 StackExchange API、MSDN 和 *** 上的几个示例,但我无法让其中任何一个为我工作。

实现这一目标的最佳方法是什么,我是否走在正确的轨道上?

谢谢大家。

【问题讨论】:

"最好的方法是返回 JSON 对象并将其存储在一个数组或字节中" 请注意,字符串是一个字节数组。 【参考方案1】:

只需像这样实例化 HttpClient:

HttpClientHandler handler = new HttpClientHandler()

    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
;

using (var client = new HttpClient(handler)) //see update below

    // your code

2020 年 6 月 19 日更新: 不建议在 'using' 块中使用 httpclient,因为它可能会导致端口耗尽。

private static HttpClient client = null;
    
ContructorMethod()

   if(client == null)
   
        HttpClientHandler handler = new HttpClientHandler()
        
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        ;        
        client = new HttpClient(handler);
   
// your code            
 

如果使用 .Net Core 2.1+,请考虑使用 IHttpClientFactory 并像这样在您的启动代码中注入。

 var timeout = Policy.TimeoutAsync<HttpResponseMessage>(
            TimeSpan.FromSeconds(60));

 services.AddHttpClient<XApiClient>().ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
        
            AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
        ).AddPolicyHandler(request => timeout);

【讨论】:

@FoxDeploy 使用此解决方案时,无需更改代码即可获取内容。请参阅此处以供参考:***.com/questions/26597665/… 虽然是老帖子,但这个答案刚刚解决了我在 .netcore 中的问题,从 1.1 移动到 2.0 似乎客户端自动进行了解压,所以我不得不在 2.0让它工作......谢谢! 只是为了背靠@SebastianCastaldi,但.net core 1.1 正确设置了AutomaticDecompression,但在.net core 2.0 中它设置为NONE。这花了我太长时间才弄清楚...... 注意:HttpClient不应该在using中使用 np!当我们集群中的主机上的每个 VM 停止工作并抱怨端口时,我学到了很难的方法。花了数周时间才发现这是一个没有正确使用 HttpClient 的应用程序。【参考方案2】:

我使用下面链接中的代码来解压缩 GZip 流。然后使用解压缩的字节数组来获取所需的 JSON 对象。希望它可以帮助一些人。

var readTask = result.Content.ReadAsByteArrayAsync().Result;
var decompressedData = Decompress(readTask);
string jsonString = System.Text.Encoding.UTF8.GetString(decompressedData, 0, decompressedData.Length);
ResponseObjectClass responseObject = Newtonsoft.Json.JsonConvert.DeserializeObject<ResponseObjectClass>(jsonString);

https://www.dotnetperls.com/decompress

static byte[] Decompress(byte[] gzip)

    using (GZipStream stream = new GZipStream(new MemoryStream(gzip), CompressionMode.Decompress))
    
        const int size = 4096;
        byte[] buffer = new byte[size];
        using (MemoryStream memory = new MemoryStream())
        
            int count = 0;
            do
            
                count = stream.Read(buffer, 0, size);
                if (count > 0)
                
                    memory.Write(buffer, 0, count);
                
            
            while (count > 0);
            return memory.ToArray();
        
    

【讨论】:

【参考方案3】:

好的,我最终解决了我的问题。如果有更好的方法请告诉我:-)

        public DataSet getData(string strFoo)
    
        string url = "foo";

        HttpClient client = new HttpClient();
        HttpResponseMessage response;   
        DataSet dsTable = new DataSet();
        try
        
               //Gets the headers that should be sent with each request
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
              //Returned JSON
            response = client.GetAsync(url).Result;
              //converts JSON to string
            string responseJSONContent = response.Content.ReadAsStringAsync().Result;
              //deserializes string to list
            var jsonList = DeSerializeJsonString(responseJSONContent);
              //converts list to dataset. Bad name I know.
            dsTable = Foo_ConnectAPI.ExtentsionHelpers.ToDataSet<RootObject>(jsonList);
              //Returns the dataset                
            return dsTable;
        
        catch (Exception ex)
        
            System.Windows.Forms.MessageBox.Show(ex.Message);
            return null;
        
    

       //deserializes the string to a list. Utilizes JSON.net. RootObject is a class that contains the get and set for the JSON elements

    public List<RootObject> DeSerializeJsonString(string jsonString)
    
          //Initialized the List
        List<RootObject> list = new List<RootObject>();
          //json.net deserializes string
        list = (List<RootObject>)JsonConvert.DeserializeObject<List<RootObject>>(jsonString);

        return list;
    

RootObject 包含将获取 JSON 值的 get 集。

public class RootObject
  
      //These string will be set to the elements within the JSON. Each one is directly mapped to the JSON elements.
      //This only takes into account a JSON that doesn't contain nested arrays
    public string EntityID  get; set; 

    public string Address1  get; set; 

    public string Address2  get; set; 

    public string Address3  get; set; 


创建上述类的最简单方法是使用json2charp,它将相应地格式化并提供正确的数据类型。

以下来自***上的另一个答案 再次,它没有考虑嵌套的 JSON。

    internal static class ExtentsionHelpers

    public static DataSet ToDataSet<T>(this List<RootObject> list)
    
        try
        
            Type elementType = typeof(RootObject);
            DataSet ds = new DataSet();
            DataTable t = new DataTable();
            ds.Tables.Add(t);

            try
            
                //add a column to table for each public property on T
                foreach (var propInfo in elementType.GetProperties())
                
                    try
                    
                        Type ColType = Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType;

                            t.Columns.Add(propInfo.Name, ColType);

                    
                    catch (Exception ex)
                    
                        System.Windows.Forms.MessageBox.Show(ex.Message);
                    

                
            
            catch (Exception ex)
            
                System.Windows.Forms.MessageBox.Show(ex.Message);
            

            try
            
                //go through each property on T and add each value to the table
                foreach (RootObject item in list)
                
                    DataRow row = t.NewRow();

                    foreach (var propInfo in elementType.GetProperties())
                    
                        row[propInfo.Name] = propInfo.GetValue(item, null) ?? DBNull.Value;
                    

                    t.Rows.Add(row);
                
            
            catch (Exception ex)
            
                System.Windows.Forms.MessageBox.Show(ex.Message);
            

            insert.insertCategories(t);
            return ds.
        
        catch (Exception ex)
        
            System.Windows.Forms.MessageBox.Show(ex.Message);

            return null;
        
    
;

然后最后将上述数据集插入到一个表中,其中的列映射到 JSON,我使用 SQL 批量复制和以下类

public class insert
 
    public static string insertCategories(DataTable table)
         
        SqlConnection objConnection = new SqlConnection();
          //As specified in the App.config/web.config file
        objConnection.ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["foo"].ToString();

        try
                                         
            objConnection.Open();
            var bulkCopy = new SqlBulkCopy(objConnection.ConnectionString);

            bulkCopy.DestinationTableName = "dbo.foo";
            bulkCopy.BulkCopyTimeout = 600;
            bulkCopy.WriteToServer(table);

            return "";
        
        catch (Exception ex)
        
            System.Windows.Forms.MessageBox.Show(ex.Message);
            return "";
        
        finally
        
            objConnection.Close();
                 
    
;

因此,上述方法可以将来自 webAPI 的 JSON 插入数据库。这是我开始工作的事情。但我绝不指望它是完美的。如果您有任何改进,请相应更新。

【讨论】:

您应该在using() 语句中分别创建您的HttpClientHttpResponse,以确保正确、及时地处理和关闭底层流。

以上是关于从 HTTPClient 响应中解压 GZip 流的主要内容,如果未能解决你的问题,请参考以下文章

使用 HttpClient、REST 和 gzip 读取 JSON

c#:如何使用 httpclient 发布异步请求并获取流?

用C#解码PHP中的GZIP流

HttpClient AutomaticDecompression 与 gzip 一起使用,而不是放气

GZIP是浏览器自动解压的吗?

HttpClient在响应流完全读取之前被释放,导致连接关闭异常