为何XMLHttpRequest的event.total始终为0,仅Content-Type为PDF时正常?
问题核心原因
你遇到的问题是Apache的mod_deflate(gzip压缩模块)的默认规则导致的:
- Apache默认会对所有
text/*类型的响应(比如你设置的text/plain)自动执行gzip压缩 - 压缩是边传输边生成的,无法提前计算最终压缩后的文件大小,所以Apache会自动删除你代码里设置的
Content-Length响应头,替换为Transfer-Encoding: chunked分块传输 - 而
application/pdf、application/octet-stream这类非文本的MIME类型,不在mod_deflate的默认压缩列表里,所以不会被压缩,你设置的Content-Length头可以正常保留,前端就能拿到event.total的值
你控制台返回的响应头里同时存在content-encoding: gzip和transfer-encoding: chunked,已经验证了这个判断。
可行的解决办法
如果你需要保留text/plain类型,又想正常拿到下载进度,可以选择以下任意一种方案:
方案1:针对该下载接口关闭gzip压缩
可以在backend.php代码里加一行响应头,主动告诉Apache不要压缩当前响应:
header('Content-Encoding: none');
也可以在.htaccess里添加规则,针对该后端路径关闭压缩:
SetEnvIf Request_URI ^/backend.php no-gzip dont-vary
方案2:保留压缩,自定义头传递原始文件大小
如果你需要保留gzip压缩节省带宽,可以在后端额外加一个自定义头传递原始文件大小,前端从自定义头里取值当total:
后端PHP添加:
header('X-Original-Content-Length: ' . filesize($file));
然后别忘了在.htaccess的Access-Control-Expose-Headers里加上这个自定义头:
Header add Access-Control-Expose-Headers "Content-Length, X-Original-Content-Length"
前端进度事件里直接取这个头的值即可:
const total = oReq.getResponseHeader('X-Original-Content-Length')
方案3:修改Apache的mod_deflate压缩规则,排除你需要的文本类型(不推荐)
这种方式会影响全局所有同类型响应,没必要单独为下载接口调整。
内容的提问来源于stack exchange,提问作者Paflow
相关产品推荐
相关产品推荐

