从 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()
语句中分别创建您的HttpClient
和HttpResponse
,以确保正确、及时地处理和关闭底层流。以上是关于从 HTTPClient 响应中解压 GZip 流的主要内容,如果未能解决你的问题,请参考以下文章
使用 HttpClient、REST 和 gzip 读取 JSON
c#:如何使用 httpclient 发布异步请求并获取流?