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

gzip压缩下fetch()响应如何获取正确长度实现进度条

Fetch 实现gzip压缩资源下载进度的可行方案

核心结论

浏览器原生Fetch API暴露的response.body是自动完成gzip/deflate/br解压后的数据流,没有任何前端API可以获取网络层实际传输的压缩后字节数,因此「统计已下载压缩数据大小和content-length比对」的诉求无法在纯前端层面实现。
要实现准确的进度条,只能走「获取解压后数据总长度,和读取到的解压后字节数比对」的路径,落地方式如下:

落地方案

方案1:服务端返回自定义头标识解压后大小(兼容性100%,推荐优先使用)

服务端在生成压缩响应时,额外新增自定义响应头返回资源未压缩的真实字节长度,例如x-decompressed-content-length: 1228800(对应1200KB解压后大小)。
前端读取该头作为进度计算的总长度即可,跨域场景需要在服务端CORS配置的Access-Control-Expose-Headers中暴露该自定义头,否则前端无法读取。
修正后的代码示例:

fetch("http://example.com/bigdata.json")
    .then((response) => {
        if (!response.body) return;
        const reader = response.body.getReader();
        let download_done = 0;
        // 优先读取解压后总长度,未开启压缩场景降级取默认content-length
        const download_size = parseInt(
            response.headers.get("x-decompressed-content-length") 
            ?? response.headers.get("content-length")
        );

        return new ReadableStream({
            start(controller) {
                function push() {
                    reader.read().then(({ done, value }) => {
                        if (done) {
                            controller.close();
                            download_size && !Number.isNaN(download_size) && report_progress(download_size, download_size);
                            return;
                        }
                        if (value) {
                            download_done += value.byteLength;
                            if (download_size && !Number.isNaN(download_size)) {
                                report_progress(download_done, download_size);
                            } else {
                                // 无有效总长度时,切换为不确定状态的加载样式
                                show_indeterminate_loading();
                            }
                        }
                        controller.enqueue(value);
                        push();
                    });
                }
                push();
            },
        });
    })
    .then((stream) => {
        return new Response(stream, {
            headers: { "Content-Type": "application/json" },
        }).json();
    })
    .then(function (result) {
        console.groupCollapsed("api_request(bigdata.json)");
        console.log(result);
        console.groupEnd();
    })

方案2:构建阶段预埋资源大小映射(适合静态资源场景)

如果请求的是站点自身托管的静态资源,可以在项目构建阶段生成一份资源清单,记录每个静态资源压缩后的路径、对应的解压后实际大小。前端发起请求前先查清单拿到对应资源的解压后总长度,同样可以实现准确进度计算,不需要修改服务端响应逻辑。

避坑说明

  • XMLHttpRequest的progress事件存在完全相同的问题:total字段取content-length的压缩后大小,loaded字段在现代浏览器中返回解压后字节数,同样会出现进度超过100%的错误,无法用来解决该问题。
  • 所有尝试在前端手动拦截压缩流、统计压缩字节数的方案均不可行:浏览器禁止前端修改Accept-Encoding请求头,网络层的解压过程对上层JS完全黑盒,没有规范内的绕过方式。
  • 如果既无法修改服务端配置,也拿不到资源的预计算大小,不要强行实现精确进度条,改用不确定状态的加载动效即可,避免出现进度超过100%的异常体验。

内容的提问来源于stack exchange,提问作者Shish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 21:33:24