POST请求下载大JSON文件加进度条:无法获取内容长度
POST请求下载大JSON无法获取ContentLength?这样解决进度条问题
问题原因
你遇到的ContentLength返回-1的情况,通常是因为服务器采用了**分块传输编码(Transfer-Encoding: chunked)**返回数据。当服务器动态生成大JSON时,它无法提前知道最终的总数据大小,所以不会设置Content-Length响应头,这时候HttpWebResponse.ContentLength就会返回-1,导致进度条无法确定总进度。
解决方案
根据实际情况,你可以选择以下几种方案:
方案1:协调服务器端添加响应头(最优解)
如果能联系到服务器开发人员,让他们在返回响应时添加Content-Length头,或者自定义一个比如X-Total-Size的响应头来告知JSON的总大小,这样客户端就能直接拿到总大小,正常显示进度条。
方案2:客户端侧的替代处理(无需服务器修改)
如果服务器无法修改,我们可以调整UI逻辑,让用户依然能感知下载状态:
- 把进度条设置为滚动模式(Marquee),提示用户正在下载;
- 实时显示已下载的字节数,让用户知道下载在正常进行;
- 尝试发送HEAD请求(如果服务器支持POST接口的HEAD请求),提前获取总大小,但很多POST接口不支持HEAD,这个方法不一定可行。
修改后的代码示例
我修正了你代码里的一些小问题(比如JSON格式错误、手动关闭流的冗余操作),并加入了上述的处理逻辑:
public void DownloadJson(string url, string userName, string password, string fileDownload) { HttpWebRequest httpRequest; HttpWebResponse httpResponse; int totalSize = -1; // 修正原代码的JSON格式错误,添加闭合引号 var json = $"{{\"user\":\"{userName}\",\"pwd\":\"{password}\"}}"; httpRequest = (HttpWebRequest)WebRequest.Create(url); httpRequest.Method = WebRequestMethods.Http.Post; httpRequest.ContentLength = json.Length; httpRequest.ContentType = "application/json"; httpRequest.Timeout = 600 * 60 * 1000; // 建议用UTF8编码,避免非ASCII字符乱码 var data = Encoding.UTF8.GetBytes(json); using (var s = httpRequest.GetRequestStream()) { s.Write(data, 0, data.Length); // using块会自动释放流,无需手动Close } httpResponse = (HttpWebResponse)httpRequest.GetResponse(); // 先尝试获取标准Content-Length if (httpResponse.ContentLength > 0) { totalSize = (int)httpResponse.ContentLength; progressBar.Invoke((MethodInvoker)(() => { progressBar.Maximum = totalSize; progressBar.Style = ProgressBarStyle.Blocks; })); } // 尝试读取自定义响应头(如果服务器支持) else { var totalSizeHeader = httpResponse.Headers["X-Total-Size"]; if (!string.IsNullOrEmpty(totalSizeHeader) && int.TryParse(totalSizeHeader, out int customTotalSize)) { totalSize = customTotalSize; progressBar.Invoke((MethodInvoker)(() => { progressBar.Maximum = totalSize; progressBar.Style = ProgressBarStyle.Blocks; })); } // 都获取不到的话,切换到滚动进度条模式 else { progressBar.Invoke((MethodInvoker)(() => { progressBar.Style = ProgressBarStyle.Marquee; })); } } Stream rs = httpResponse.GetResponseStream(); using (FileStream fs = File.Create(fileDownload)) { byte[] buffer = new byte[16 * 1024]; int read; long downloadedBytes = 0; // 用long避免大文件溢出 using (rs) { while ((read = rs.Read(buffer, 0, buffer.Length)) > 0) { fs.Write(buffer, 0, read); downloadedBytes += read; if (totalSize > 0) { // 有总大小的话更新进度条 progressBar.Invoke((MethodInvoker)(() => { progressBar.Value = (int)downloadedBytes; })); } // 没有总大小的话,显示已下载字节数 Console.WriteLine($"已接收字节数: {downloadedBytes:N0}"); // 也可以在UI上更新标签: // labelDownloadStatus.Invoke((MethodInvoker)(() => labelDownloadStatus.Text = $"已接收: {downloadedBytes:N0} 字节")); } } } httpResponse.Close(); }
额外提示
- 原代码中的JSON字符串少了一个闭合引号,这会导致请求参数格式错误,已经修正;
- 尽量使用
Encoding.UTF8代替ASCII,避免用户名或密码包含中文、特殊字符时出现乱码; - 如果下载的文件非常大,建议使用
long类型来记录已下载字节数,避免int溢出。
内容的提问来源于stack exchange,提问作者ppau2004
相关产品推荐
相关产品推荐

