告别大请求卡顿!原生 CompressionStream 实现 axios 请求体自动 Gzip 压缩(前后端全适配)

张开发
2026/4/14 12:58:45 15 分钟阅读

分享文章

告别大请求卡顿!原生 CompressionStream 实现 axios 请求体自动 Gzip 压缩(前后端全适配)
前端请求体 Gzip 压缩最佳实践原生 CompressionStream axios 全自动方案前言在后台管理系统、批量导入导出、大数据表单提交、大批量表格保存等场景中我们经常遇到请求体过大导致接口缓慢、上传耗时、甚至超时失败的问题。大部分开发者只熟悉后端对响应体开启 Gzip却忽略了请求体同样可以压缩而且在大数据提交场景下收益往往更高。本文将带你使用浏览器原生CompressionStreamAPI结合 axios 拦截器实现一套零依赖、高性能、全自动的请求体 Gzip 压缩方案大幅降低传输体积提升接口速度与系统稳定性。一、实现思路只对 POST 请求 普通 JSON 对象进行压缩计算请求体大小超过阈值再压缩避免小请求浪费性能使用浏览器原生CompressionStream做 Gzip 压缩自动添加请求头Content-Encoding: gzip标识格式接入 axios 全局拦截器业务代码无感使用二、核心工具函数封装1. 判断是否为普通对象排除 FormData、Blob、File、ArrayBuffer 等不需要/无法压缩的类型。/** * 判断是否为普通对象可 JSON 序列化 */constisPlainObject(data){returntypeofdataobjectdata!null!ArrayBuffer.isView(data)!(datainstanceofFormData)!(datainstanceofBlob)!(datainstanceofFile)!(datainstanceofArrayBuffer);};2. 判断是否为 POST 请求/** * 判断是否 POST 请求 */constisPostMethod(axiosConfig){returnaxiosConfig.method?.toLowerCase()post;};3. 判断请求体是否可压缩/** * 判断请求体是否为普通对象 */constisPlainRequestBody(axiosConfig){constbodyDataaxiosConfig.data;returnisPlainObject(bodyData);};4. 计算请求体大小支持 byte/kb/mb/** * 计算请求体大小 * param data 请求数据 * param measuringType 单位 byte/kb/mb */constgetRequestBodySize(data,measuringTypemb){if(!data)return0;letbyteLength0;if(typeofdatastring){byteLengthnewBlob([data]).size;}elseif(isPlainObject(data)){try{constjsonStringJSON.stringify(data);byteLengthnewBlob([jsonString]).size;}catch(e){console.warn(对象序列化失败,e);}}elseif(datainstanceofBlob||datainstanceofFile){byteLengthdata.size;}constunitMap{byte:1,kb:1024,mb:1024*1024};returnbyteLength/unitMap[measuringType];};三、原生 Gzip 压缩函数核心使用浏览器原生 API不依赖 pako零打包体积。/** * 原生 Gzip 压缩 * param data 待压缩对象 * returns 压缩后的 ArrayBuffer */constcompressasync(data){// 1. JSON 序列化constjsonStringJSON.stringify(data);// 2. 转为二进制 Uint8ArrayconstencodernewTextEncoder();constinputDataencoder.encode(jsonString);// 3. 创建可读流constreadableStreamnewReadableStream({start(controller){controller.enqueue(inputData);controller.close();}});// 4. 创建 gzip 压缩流constcompressionStreamnewCompressionStream(gzip);constcompressedStreamreadableStream.pipeThrough(compressionStream);// 5. 转换为 ArrayBufferreturnawaitnewResponse(compressedStream).arrayBuffer();};四、axios 自动压缩拦截器请求前自动判断大小大于 1.5MB 自动压缩。/** * axios 请求体压缩拦截器 */asyncfunctioncompressionInterceptor(config){// 浏览器兼容判断if(!window.CompressionStream){returnconfig;}// 只处理普通 POST 对象if(isPlainRequestBody(config)){constsizeMBgetRequestBodySize(config.data);// 大于 1.5MB 执行压缩if(sizeMB1.5){config.dataawaitcompress(config.data);// 添加压缩请求头config.headers[Content-Encoding]gzip;config.headers[Content-Type]application/json;}}returnconfig;}五、挂载到 axios 全局使用// 注册拦截器axios.interceptors.request.use(compressionInterceptor);六、CompressionStream 与 pako 对比对比项CompressionStream原生pako第三方库体积0 依赖0 打包体积约 45KB增加打包体积性能浏览器底层实现流式处理更快纯 JS 实现大数据略慢API 风格异步 Stream现代化架构同步调用简单直接兼容性现代浏览器支持支持 IE 等老旧浏览器适用场景中后台、移动端、现代项目需要兼容极低版本浏览器结论现代前端优先使用原生 CompressionStreampako 仅做降级兼容。七、后端如何处理 Gzip 压缩请求体前端发送Content-Encoding: gzip后后端必须解压才能正常解析 JSON否则会收到二进制乱码。1. Nginx 配置必须开启server { listen 80; server_name your.domain.com; # 开启请求体解压 gunzip on; gunzip_buffers 16 8k; # 请求体大小限制 client_max_body_size 20m; client_body_buffer_size 20m; }2. SpringBoot 处理Java过滤器自动解压 GzipConfigurationpublicclassGzipRequestFilterimplementsFilter{OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{HttpServletRequestreq(HttpServletRequest)request;Stringencodingreq.getHeader(Content-Encoding);if(encoding!nullencoding.contains(gzip)){GzipRequestWrapperwrappernewGzipRequestWrapper(req);chain.doFilter(wrapper,response);}else{chain.doFilter(request,response);}}}解压包装类 GzipRequestWrapperimportorg.apache.commons.io.IOUtils;importjavax.servlet.*;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletRequestWrapper;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.util.zip.GZIPInputStream;publicclassGzipRequestWrapperextendsHttpServletRequestWrapper{privatefinalbyte[]body;publicGzipRequestWrapper(HttpServletRequestrequest)throwsIOException{super(request);try(GZIPInputStreamgzipInnewGZIPInputStream(request.getInputStream());ByteArrayOutputStreamoutnewByteArrayOutputStream()){IOUtils.copy(gzipIn,out);bodyout.toByteArray();}}OverridepublicServletInputStreamgetInputStream(){ByteArrayInputStreambaisnewByteArrayInputStream(body);returnnewServletInputStream(){Overridepublicintread(){returnbais.read();}OverridepublicbooleanisFinished(){returnbais.available()0;}OverridepublicbooleanisReady(){returntrue;}OverridepublicvoidsetReadListener(ReadListenerlistener){}};}}3. Node.js (Express/Koa)constexpressrequire(express);constcompressionrequire(compression);constappexpress();// 自动解压 gzip 请求体app.use(compression());app.use(express.json({limit:20mb}));八、完整注意事项生产必看后端必须配置解压否则收到二进制乱码前端压缩无意义。Nginx 必须开启gunzip on否则不会解压请求体。不要压缩文件类数据FormData、Blob、File、图片、音频等本身已压缩再压缩无收益。阈值不要过低建议 ≥ 256KB 或 1MB过小数据压缩反而耗性能。注意配置请求体大小限制client_max_body_size、服务端请求体大小限制避免 413 错误。浏览器兼容处理低版本浏览器不支持CompressionStream需加判断跳过。axios 拦截器为 async 函数必须 return config否则请求会中断。必须设置Content-Type: application/json否则后端可能无法自动解析。避免链路重复压缩只在前端或网关其中一处做 Gzip。本地调试可临时关闭压缩方便在 Network 查看明文请求体。超大数据10MB建议配合分片、分批、异步导入不依赖单一压缩。生产环境建议使用HTTPS避免压缩内容被中间人劫持或篡改。九、效果总结JSON 数据压缩率通常可达60%~80%大数据提交、批量保存接口速度明显提升接口超时、上传失败概率大幅降低零第三方依赖不增加打包体积接入简单、全局生效、业务无侵入总结本文实现的前端请求体全自动 Gzip 压缩方案基于浏览器原生CompressionStream配合 axios 拦截器实现按体积智能压缩是中后台系统、大数据交互场景下非常实用的高阶优化手段。配合后端 Nginx 服务端解压配置可真正实现前后端一体化提速大幅提升用户体验与系统稳定性。

更多文章