You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 10:02:21