如何处理 Transfer-Encoding:使用 .NET Core 下载文件时分块 HttpClient.PostAsync
How to handle Transfer-Encoding: chunked when downloading a file with .NET Core HttpClient.PostAsync
情况
我正在使用 HttpClient(System.Net.Http,版本=4.2.1.0)向 Web API 发送包含多部分表单数据的 HTTP 请求 POST。表单数据包括一个字符串参数(benchmark
)和一个包含在stream
中的文件(addressFile
)。 API 调用 returns 我想保存到磁盘的 CSV 文件。
响应包含 header Transfer-Encoding: chunked
和 responseBytes
中包含的数据 包括 块 header。我会 期望 HttpClient 库删除这些 headers,它们是实际内容的 metadata。相反,它只是在 Content
中包含 header 行。
问题
处理这些块 header 的正确方法是什么?
我当然可以自己编写一个方法来处理 header,但我很难相信 HttpClient 库还没有在某处内置此功能。
代码
using (var client = new HttpClient())
{
var content = new MultipartFormDataContent();
content.Add(new StringContent("Public_AR_Current"), "benchmark");
content.Add(new ByteArrayContent(stream.ToArray()), "addressFile", "addressFile.csv");
var response = await client.PostAsync("https://geocoding.geo.census.gov/geocoder/locations/addressbatch", content);
var responseBytes = await response.Content.ReadAsByteArrayAsync();
saveResponse(responseBytes);
var geocodedItems = ParseGeocodeResponse(responseBytes);
var parsedItems = geocodedItems.Select(gi => gi.ToEpaHandlerUsCensusGeocode());
return parsedItems;
}
结果
注意第一行和后续行中的块 header(0fe8
、0060
、0fe8
)。
0fe8
0fe8
"AK0000036228","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","Match","Exact","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","-149.87424,61.23034","190797469","R"
"AK0000363994","3155 E 18TH CIR, ANCHORAGE, AK, 99508","Match","Non_Exact","3155 E 18TH CIR, ANCHORAGE, AK, 99508","-149.82193,61.20462","190799569","L"
...
0060
28712","N 65 DEG 35 15 W 167 DEG 55 18, WALES, AK, 99734","No_Match"
"AK0000112227","KODIAK ARPR
...
0fe8
T AREA, KODIAK, AK, 99615","No_Match"
"AK0000033902","2130 E DIMOND BLVD, ANCHORAGE, AK, 99515","Match","Non_Exact","2130 W DIMOND BLVD, ANCHORAGE, AK, 99515","-149.91881,61.1375","190795925","L"
"AK0000562769","3100 TONGASS AVE, KETCHIKAN, AK, 99901-5746","No_Match"
预期结果
我希望 HttpClient 库删除 headers。
"AK0000036228","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","Match","Exact","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","-149.87424,61.23034","190797469","R"
"AK0000363994","3155 E 18TH CIR, ANCHORAGE, AK, 99508","Match","Non_Exact","3155 E 18TH CIR, ANCHORAGE, AK, 99508","-149.82193,61.20462","190799569","L"
"AK0000228718","1050 ASPEN ST, FAIRBANKS, AK, 99709-5501","Match","Exact","1050 ASPEN ST, FAIRBANKS, AK, 99709","-147.7731,64.8535","605310042","L"
"AK0000536714","SMITH COVE IN SMITH LAGOON T74S R86E CRM S17 & 20, KASAAN, AK, 99901","No_Match"
"AK0001413822","USS-12403, N BANK WOOD RIVER, ALEKNAGIK, AK, 99555","No_Match"
"AK0000489567","BREAKWATER BTWN WESTERN AVE & TAIT ST, METLAKATLA, AK, 99926","No_Match"
我最终编写了这个扩展方法,它对我的用例表现足够好。
public static Task<Stream> ReadAsStreamAsync(this HttpContent content, bool isChunked)
{
if (!isChunked)
{
return content.ReadAsStreamAsync();
}
else
{
var task = content.ReadAsStreamAsync()
.ContinueWith<Stream>((streamTask) =>
{
var outputStream = new MemoryStream();
var buffer = new char[1024 * 1024];
var stream = streamTask.Result;
// No using() so that we don't dispose stream.
var tr = new StreamReader(stream);
var tw = new StreamWriter(outputStream);
while (!tr.EndOfStream)
{
var chunkSizeStr = tr.ReadLine().Trim();
var chunkSize = int.Parse(chunkSizeStr, System.Globalization.NumberStyles.HexNumber);
tr.ReadBlock(buffer, 0, chunkSize);
tw.Write(buffer, 0, chunkSize);
tr.ReadLine();
}
return outputStream;
});
return task;
}
}
情况
我正在使用 HttpClient(System.Net.Http,版本=4.2.1.0)向 Web API 发送包含多部分表单数据的 HTTP 请求 POST。表单数据包括一个字符串参数(benchmark
)和一个包含在stream
中的文件(addressFile
)。 API 调用 returns 我想保存到磁盘的 CSV 文件。
响应包含 header Transfer-Encoding: chunked
和 responseBytes
中包含的数据 包括 块 header。我会 期望 HttpClient 库删除这些 headers,它们是实际内容的 metadata。相反,它只是在 Content
中包含 header 行。
问题
处理这些块 header 的正确方法是什么?
我当然可以自己编写一个方法来处理 header,但我很难相信 HttpClient 库还没有在某处内置此功能。
代码
using (var client = new HttpClient())
{
var content = new MultipartFormDataContent();
content.Add(new StringContent("Public_AR_Current"), "benchmark");
content.Add(new ByteArrayContent(stream.ToArray()), "addressFile", "addressFile.csv");
var response = await client.PostAsync("https://geocoding.geo.census.gov/geocoder/locations/addressbatch", content);
var responseBytes = await response.Content.ReadAsByteArrayAsync();
saveResponse(responseBytes);
var geocodedItems = ParseGeocodeResponse(responseBytes);
var parsedItems = geocodedItems.Select(gi => gi.ToEpaHandlerUsCensusGeocode());
return parsedItems;
}
结果
注意第一行和后续行中的块 header(0fe8
、0060
、0fe8
)。
0fe8
0fe8
"AK0000036228","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","Match","Exact","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","-149.87424,61.23034","190797469","R"
"AK0000363994","3155 E 18TH CIR, ANCHORAGE, AK, 99508","Match","Non_Exact","3155 E 18TH CIR, ANCHORAGE, AK, 99508","-149.82193,61.20462","190799569","L"
...
0060
28712","N 65 DEG 35 15 W 167 DEG 55 18, WALES, AK, 99734","No_Match"
"AK0000112227","KODIAK ARPR
...
0fe8
T AREA, KODIAK, AK, 99615","No_Match"
"AK0000033902","2130 E DIMOND BLVD, ANCHORAGE, AK, 99515","Match","Non_Exact","2130 W DIMOND BLVD, ANCHORAGE, AK, 99515","-149.91881,61.1375","190795925","L"
"AK0000562769","3100 TONGASS AVE, KETCHIKAN, AK, 99901-5746","No_Match"
预期结果
我希望 HttpClient 库删除 headers。
"AK0000036228","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","Match","Exact","500 HOLLYWOOD DR, ANCHORAGE, AK, 99501","-149.87424,61.23034","190797469","R"
"AK0000363994","3155 E 18TH CIR, ANCHORAGE, AK, 99508","Match","Non_Exact","3155 E 18TH CIR, ANCHORAGE, AK, 99508","-149.82193,61.20462","190799569","L"
"AK0000228718","1050 ASPEN ST, FAIRBANKS, AK, 99709-5501","Match","Exact","1050 ASPEN ST, FAIRBANKS, AK, 99709","-147.7731,64.8535","605310042","L"
"AK0000536714","SMITH COVE IN SMITH LAGOON T74S R86E CRM S17 & 20, KASAAN, AK, 99901","No_Match"
"AK0001413822","USS-12403, N BANK WOOD RIVER, ALEKNAGIK, AK, 99555","No_Match"
"AK0000489567","BREAKWATER BTWN WESTERN AVE & TAIT ST, METLAKATLA, AK, 99926","No_Match"
我最终编写了这个扩展方法,它对我的用例表现足够好。
public static Task<Stream> ReadAsStreamAsync(this HttpContent content, bool isChunked)
{
if (!isChunked)
{
return content.ReadAsStreamAsync();
}
else
{
var task = content.ReadAsStreamAsync()
.ContinueWith<Stream>((streamTask) =>
{
var outputStream = new MemoryStream();
var buffer = new char[1024 * 1024];
var stream = streamTask.Result;
// No using() so that we don't dispose stream.
var tr = new StreamReader(stream);
var tw = new StreamWriter(outputStream);
while (!tr.EndOfStream)
{
var chunkSizeStr = tr.ReadLine().Trim();
var chunkSize = int.Parse(chunkSizeStr, System.Globalization.NumberStyles.HexNumber);
tr.ReadBlock(buffer, 0, chunkSize);
tw.Write(buffer, 0, chunkSize);
tr.ReadLine();
}
return outputStream;
});
return task;
}
}