前端请求体 Gzip 压缩最佳实践:原生 CompressionStream + axios 全自动方案
前言
在后台管理系统、批量导入导出、大数据表单提交、大批量表格保存等场景中,我们经常遇到请求体过大导致接口缓慢、上传耗时、甚至超时失败的问题。
大部分开发者只熟悉后端对响应体开启 Gzip,却忽略了:请求体同样可以压缩,而且在大数据提交场景下收益往往更高。
本文将带你使用浏览器原生 CompressionStream API,结合 axios 拦截器,实现一套零依赖、高性能、全自动的请求体 Gzip 压缩方案,大幅降低传输体积,提升接口速度与系统稳定性。
一、实现思路
- 只对 POST 请求 + 普通 JSON 对象进行压缩
- 计算请求体大小,超过阈值再压缩,避免小请求浪费性能
- 使用浏览器原生
CompressionStream做 Gzip 压缩 - 自动添加请求头
Content-Encoding: gzip标识格式 - 接入 axios 全局拦截器,业务代码无感使用
二、核心工具函数封装
1. 判断是否为普通对象
排除 FormData、Blob、File、ArrayBuffer 等不需要/无法压缩的类型。
javascript
/**
* 判断是否为普通对象(可 JSON 序列化)
*/
const isPlainObject = (data) => {
return typeof data === 'object' &&
data !== null &&
!ArrayBuffer.isView(data) &&
!(data instanceof FormData) &&
!(data instanceof Blob) &&
!(data instanceof File) &&
!(data instanceof ArrayBuffer);
};
2. 判断是否为 POST 请求
javascript
/**
* 判断是否 POST 请求
*/
const isPostMethod = (axiosConfig) => {
return axiosConfig.method?.toLowerCase() === 'post';
};
3. 判断请求体是否可压缩
javascript
/**
* 判断请求体是否为普通对象
*/
const isPlainRequestBody = (axiosConfig) => {
const bodyData = axiosConfig.data;
return isPlainObject(bodyData);
};
4. 计算请求体大小(支持 byte/kb/mb)
javascript
/**
* 计算请求体大小
* @param data 请求数据
* @param measuringType 单位 byte/kb/mb
*/
const getRequestBodySize = (data, measuringType = 'mb') => {
if (!data) return 0;
let byteLength = 0;
if (typeof data === 'string') {
byteLength = new Blob([data]).size;
} else if (isPlainObject(data)) {
try {
const jsonString = JSON.stringify(data);
byteLength = new Blob([jsonString]).size;
} catch (e) {
console.warn('对象序列化失败', e);
}
} else if (data instanceof Blob || data instanceof File) {
byteLength = data.size;
}
const unitMap = {
byte: 1,
kb: 1024,
mb: 1024 * 1024
};
return byteLength / unitMap[measuringType];
};
三、原生 Gzip 压缩函数(核心)
使用浏览器原生 API,不依赖 pako,零打包体积。
javascript
/**
* 原生 Gzip 压缩
* @param data 待压缩对象
* @returns 压缩后的 ArrayBuffer
*/
const compress = async (data) => {
// 1. JSON 序列化
const jsonString = JSON.stringify(data);
// 2. 转为二进制 Uint8Array
const encoder = new TextEncoder();
const inputData = encoder.encode(jsonString);
// 3. 创建可读流
const readableStream = new ReadableStream({
start(controller) {
controller.enqueue(inputData);
controller.close();
}
});
// 4. 创建 gzip 压缩流
const compressionStream = new CompressionStream('gzip');
const compressedStream = readableStream.pipeThrough(compressionStream);
// 5. 转换为 ArrayBuffer
return await new Response(compressedStream).arrayBuffer();
};
四、axios 自动压缩拦截器
请求前自动判断大小,大于 1.5MB 自动压缩。
javascript
/**
* axios 请求体压缩拦截器
*/
async function compressionInterceptor(config) {
// 浏览器兼容判断
if (!window.CompressionStream) {
return config;
}
// 只处理普通 POST 对象
if (isPlainRequestBody(config)) {
const sizeMB = getRequestBodySize(config.data);
// 大于 1.5MB 执行压缩
if (sizeMB > 1.5) {
config.data = await compress(config.data);
// 添加压缩请求头
config.headers['Content-Encoding'] = 'gzip';
config.headers['Content-Type'] = 'application/json';
}
}
return config;
}
五、挂载到 axios 全局使用
javascript
// 注册拦截器
axios.interceptors.request.use(compressionInterceptor);
六、CompressionStream 与 pako 对比
| 对比项 | CompressionStream(原生) | pako(第三方库) |
|---|---|---|
| 体积 | 0 依赖,0 打包体积 | 约 45KB,增加打包体积 |
| 性能 | 浏览器底层实现,流式处理更快 | 纯 JS 实现,大数据略慢 |
| API 风格 | 异步 Stream,现代化架构 | 同步调用,简单直接 |
| 兼容性 | 现代浏览器支持 | 支持 IE 等老旧浏览器 |
| 适用场景 | 中后台、移动端、现代项目 | 需要兼容极低版本浏览器 |
结论:现代前端优先使用原生 CompressionStream,pako 仅做降级兼容。
七、后端如何处理 Gzip 压缩请求体
前端发送 Content-Encoding: gzip 后,后端必须解压才能正常解析 JSON,否则会收到二进制乱码。
1. Nginx 配置(必须开启)
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)
过滤器:自动解压 Gzip
java
@Configuration
public class GzipRequestFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
String encoding = req.getHeader("Content-Encoding");
if (encoding != null && encoding.contains("gzip")) {
GzipRequestWrapper wrapper = new GzipRequestWrapper(req);
chain.doFilter(wrapper, response);
} else {
chain.doFilter(request, response);
}
}
}
解压包装类 GzipRequestWrapper
java
import org.apache.commons.io.IOUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
public class GzipRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body;
public GzipRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
try (GZIPInputStream gzipIn = new GZIPInputStream(request.getInputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
IOUtils.copy(gzipIn, out);
body = out.toByteArray();
}
}
@Override
public ServletInputStream getInputStream() {
ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public int read() { return bais.read(); }
@Override public boolean isFinished() { return bais.available() == 0; }
@Override public boolean isReady() { return true; }
@Override public void setReadListener(ReadListener listener) {}
};
}
}
3. Node.js (Express/Koa)
javascript
const express = require('express');
const compression = require('compression');
const app = express();
// 自动解压 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 + 服务端解压配置,可真正实现前后端一体化提速,大幅提升用户体验与系统稳定性。