springBoot 发送报文压缩/解压

压缩:

在 Spring Boot 中配置 Controller 的请求 / 响应报文压缩,核心是通过 服务器原生配置 或 自定义拦截器 启用 Gzip/Brotli 压缩,减少网络传输量(尤其对 JSON、HTML 等文本类数据效果显著),且无需依赖 HTTP/2 环境,HTTP/1.1 也完全适用。以下是两种主流实现方案,覆盖通用场景和自定义需求:

一、核心前提

  1. 压缩适用场景:仅对 文本类数据(如 JSON、XML、HTML、CSS)有效,对二进制数据(图片、视频、文件)无效(这类数据本身已压缩,再次压缩收益极低)。
  2. 请求压缩需客户端配合:客户端需将请求体压缩后发送,并在请求头携带 Content-Encoding: gzip;服务器需配置解压逻辑。
  3. 响应压缩自动兼容:服务器压缩响应后,会在响应头添加 Content-Encoding: gzip,客户端(浏览器、Postman 等)会自动解压。

二、方案 1:服务器原生配置(推荐,零代码)

Spring Boot 内置的 Tomcat、Jetty、Undertow 服务器均支持通过配置文件启用压缩,无需修改业务代码,适用于全局压缩需求。

1. 通用配置(所有服务器通用)

在 application.yml 中添加以下配置,覆盖响应压缩(核心)和请求压缩(可选):

yaml

server:
  # 响应压缩配置(必配,优化接口返回数据)
  compression:
    enabled: true  # 开启响应压缩
    mime-types: application/json,application/xml,text/html,text/css,text/plain,application/javascript  # 需压缩的响应类型(文本类优先)
    min-response-size: 2048  # 最小响应大小(字节):小于此值不压缩(避免小数据压缩开销 > 收益)
  # 请求压缩配置(可选,处理客户端压缩的请求体)
  compression.request:
    enabled: true  # 开启请求压缩(服务器自动解压客户端发送的压缩请求)
    mime-types: application/json,application/xml  # 需解压的请求类型(仅处理文本类请求体)
    min-request-size: 1024  # 最小请求大小(字节):小于此值不解压
2. 各服务器特殊配置(精细化控制)
(1)Tomcat 服务器(默认)

若需调整压缩级别(平衡压缩速度和压缩比),可添加 Tomcat 专属配置:

yaml

server:
  compression:
    enabled: true
    # ... 通用配置不变
  tomcat:
    compression-level: 6  # 压缩级别(1-9):1=最快(压缩比低),9=最佳(压缩比高,速度慢),默认 6
(2)Undertow 服务器(支持 Brotli 压缩,比 Gzip 更优)

Undertow 可启用 Brotli 压缩(压缩率比 Gzip 高 10%-20%),适合对带宽敏感的场景:

yaml

server:
  compression:
    enabled: true
    # ... 通用配置不变
  undertow:
    compression:
      brotli:
        enabled: true  # 开启 Brotli 压缩(优先级高于 Gzip)
        level: 6  # Brotli 压缩级别(1-11),默认 6
(3)Jetty 服务器

Jetty 无需额外配置,通用压缩配置已覆盖核心功能,直接使用即可。

三、方案 2:自定义拦截器(局部压缩,精细化控制)

若需仅对 特定 Controller / 接口 启用压缩(如仅 /api/** 路径,排除文件下载接口),可通过 Spring 拦截器实现,灵活控制压缩范围。

1. 自定义响应压缩拦截器

java

运行

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

/**
 * 自定义 Gzip 响应压缩拦截器
 */
public class GzipResponseInterceptor implements HandlerInterceptor {

    // 拦截前:包装响应流,后续 Controller 输出的内容会先存入字节流
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        // 1. 判断客户端是否支持压缩(请求头携带 Accept-Encoding: gzip)
        String acceptEncoding = request.getHeader("Accept-Encoding");
        if (acceptEncoding != null && acceptEncoding.contains("gzip")) {
            // 2. 包装响应流为 Gzip 流,替换默认输出流
            GzipResponseWrapper gzipWrapper = new GzipResponseWrapper(response);
            response.setHeader("Content-Encoding", "gzip"); // 告知客户端响应已压缩
            request.setAttribute("gzipWrapper", gzipWrapper); // 存入请求域,后续关闭流
        }
        return true;
    }

    // 接口处理完成后:关闭 Gzip 流,确保压缩后的数据写入响应
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws IOException {
        GzipResponseWrapper gzipWrapper = (GzipResponseWrapper) request.getAttribute("gzipWrapper");
        if (gzipWrapper != null) {
            gzipWrapper.finishResponse(); // 输出压缩后的数据
        }
    }

    /**
     * 自定义响应包装类:将响应内容写入 Gzip 流
     */
    static class GzipResponseWrapper extends javax.servlet.http.HttpServletResponseWrapper {
        private final ByteArrayOutputStream byteStream;
        private final GZIPOutputStream gzipStream;

        public GzipResponseWrapper(HttpServletResponse response) throws IOException {
            super(response);
            this.byteStream = new ByteArrayOutputStream();
            this.gzipStream = new GZIPOutputStream(byteStream);
        }

        // 重写输出流:所有 Controller 的响应会写入 Gzip 流
        @Override
        public javax.servlet.ServletOutputStream getOutputStream() throws IOException {
            return new javax.servlet.ServletOutputStream() {
                @Override
                public void write(int b) throws IOException {
                    gzipStream.write(b);
                }

                @Override
                public boolean isReady() {
                    return true;
                }

                @Override
                public void setWriteListener(javax.servlet.WriteListener listener) {}
            };
        }

        // 完成响应:将压缩后的数据写入原始响应流
        public void finishResponse() throws IOException {
            gzipStream.finish(); // 关闭 Gzip 流,确保数据完整
            byte[] compressedData = byteStream.toByteArray();
            HttpServletResponse originalResponse = getResponse();
            originalResponse.setContentLength(compressedData.length); // 设置压缩后的数据长度
            originalResponse.getOutputStream().write(compressedData);
            originalResponse.getOutputStream().flush();
        }
    }
}
2. 注册拦截器(指定生效范围)

java

运行

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 仅对 /api/** 路径的接口启用压缩,排除文件下载接口
        registry.addInterceptor(new GzipResponseInterceptor())
                .addPathPatterns("/api/**") // 生效路径
                .excludePathPatterns("/api/file/download"); // 排除无需压缩的路径(如二进制文件)
    }
}

四、验证压缩是否生效

1. 响应压缩验证(用 Postman 或浏览器)
  • 步骤 1:发送请求时,在请求头添加 Accept-Encoding: gzip(Postman 会自动添加,可在 “Headers” 面板查看)。
  • 步骤 2:查看响应头,若包含 Content-Encoding: gzip,且响应体大小比未压缩时显著减小(如 10KB 的 JSON 压缩到 2KB),说明压缩生效。
2. 请求压缩验证(需客户端配合)
  • 客户端操作:将请求体(如 JSON)用 Gzip 压缩后发送,请求头添加 Content-Encoding: gzip
  • 服务器验证:在 Controller 中打印请求体内容,若能正常解析(而非乱码),说明服务器解压成功。

五、注意事项

  1. 避免过度压缩:小数据(如 < 2KB)无需压缩 —— 压缩过程的 CPU 开销可能大于带宽节省的收益,通过 min-response-size/min-request-size 控制。
  2. 排除二进制接口:对文件下载、图片返回等接口,需通过 excludePathPatterns 排除压缩(避免二进制数据被二次压缩导致损坏)。
  3. 压缩级别选择:生产环境推荐使用默认级别(6),平衡速度和压缩比;若服务器 CPU 资源紧张,可降低到 3-4;若带宽紧张,可提高到 7-8(不建议 9,CPU 开销过高)。

总结

  • 全局压缩:优先用方案 1(服务器原生配置),零代码、易维护,适合全量接口压缩。
  • 局部压缩:用方案 2(自定义拦截器),灵活控制生效范围,适合特定接口需求。

两种方案均兼容 HTTP/1.1,无需依赖 HTTP/2,能有效减少网络传输量,降低带宽成本,提升接口响应速度(尤其跨地域、弱网络场景)。

要不要我帮你整理一份 Spring Boot 报文压缩配置模板?包含全局配置、各服务器特殊配置、自定义拦截器代码,你可以直接复制到项目中使用。

---------------------------------------------------------------------------------------------------

解压:

客户端解压服务器的压缩响应,核心逻辑是根据服务器返回的 Content-Encoding 头,选择对应算法(如 Gzip)解压响应体,不同客户端(浏览器、Postman、自定义代码客户端)的实现方式不同,但大多无需手动处理(工具 / 框架会自动完成)。以下是常见客户端的解压方式详解:

一、核心原理:基于 HTTP 头的 “协商解压”

服务器启用响应压缩后,会在响应头中添加 Content-Encoding: gzip(或 br 表示 Brotli),告知客户端 “响应体已用该算法压缩”。客户端只需:

  1. 发送请求时在请求头携带 Accept-Encoding: gzip, br(告知服务器 “我支持这些压缩算法”);
  2. 接收响应后,检测 Content-Encoding 头,用对应算法解压响应体。

二、常见客户端的解压方式

1. 浏览器(Chrome、Firefox、Edge 等):全自动,无需手动操作

浏览器原生支持 Gzip/Brotli 解压,整个过程完全自动化:

  • 发送请求:浏览器会自动在请求头添加 Accept-Encoding: gzip, deflate, br(支持主流压缩算法);
  • 接收响应:若响应头有 Content-Encoding: gzip,浏览器会自动解压响应体,渲染页面时使用解压后的内容;
  • 验证方式:打开浏览器开发者工具(F12)→ Network 面板→ 选中任意请求→ Response Headers 中查看 Content-Encoding(有值说明压缩生效),Preview 面板显示的是解压后的正常内容(非乱码)。
2. 接口测试工具(Postman、Apifox):自动解压,可手动关闭

这类工具默认开启自动解压,无需额外配置:

  • Postman
    1. 发送请求时,工具自动添加 Accept-Encoding: gzip, deflate
    2. 接收响应后,若响应头有 Content-Encoding,会自动解压,在 Body 面板显示正常内容(非乱码);
    3. 若需关闭自动解压:点击请求设置(右上角齿轮图标)→ 取消勾选 Automatically decode gzip responses
  • Apifox:逻辑与 Postman 一致,默认自动解压,可在 “设置 - 请求设置” 中关闭。
3. 自定义代码客户端(Java、Python、JavaScript 等):需手动处理(或用框架自动处理)

若用代码编写客户端(如后端服务调用其他 API),需根据语言 / 框架特性,手动配置解压逻辑(或依赖框架原生支持)。

(1)Java 客户端(以 OkHttp 为例):框架自动解压

OkHttp 默认支持 Gzip 解压,无需手动写解压代码:

java

运行

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class JavaHttpClient {
    public static void main(String[] args) throws Exception {
        OkHttpClient client = new OkHttpClient(); // 默认启用 Gzip 解压
        
        Request request = new Request.Builder()
                .url("https://your-springboot-api.com/api/data") // 你的 Spring Boot 接口
                .build(); // OkHttp 自动添加 Accept-Encoding: gzip
        
        try (Response response = client.newCall(request).execute()) {
            // 直接获取解压后的响应体(无需手动处理)
            String responseBody = response.body().string();
            System.out.println("解压后的响应:" + responseBody);
        }
    }
}
  • 原理:OkHttp 内部会检测响应头的 Content-Encoding,若为 gzip,自动用 GZIPInputStream 解压。
(2)Python 客户端(以 requests 为例):自动解压

requests 库默认开启 Gzip 解压,逻辑与 OkHttp 类似:

python

运行

import requests

# requests 自动添加 Accept-Encoding: gzip, deflate
response = requests.get("https://your-springboot-api.com/api/data")

# 直接获取解压后的内容(response.text 已解压)
print("解压后的响应:", response.text)

# 验证是否启用了压缩:查看响应头
print("压缩算法:", response.headers.get("Content-Encoding")) # 输出 gzip
(3)JavaScript 客户端(浏览器端):自动解压

浏览器端 JS 发送请求时,fetch 或 XMLHttpRequest 会自动处理解压:

javascript

运行

// fetch 示例:自动解压
fetch("https://your-springboot-api.com/api/data")
  .then(response => response.json()) // 自动解压后解析为 JSON
  .then(data => console.log("解压后的响应:", data));

// XMLHttpRequest 示例:自动解压
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://your-springboot-api.com/api/data");
xhr.onload = function() {
  console.log("解压后的响应:", xhr.responseText); // 已自动解压
};
xhr.send();
(4)手动解压示例(Java,不用框架时):

若不用 OkHttp 等框架,需手动用 GZIPInputStream 解压:

java

运行

import java.net.HttpURLConnection;
import java.net.URL;
import java.io.GZIPInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class ManualGzipClient {
    public static void main(String[] args) throws Exception {
        URL url = new URL("https://your-springboot-api.com/api/data");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 1. 告知服务器支持 Gzip 压缩
        conn.setRequestProperty("Accept-Encoding", "gzip");
        
        // 2. 接收响应,判断是否需要解压
        String contentEncoding = conn.getHeaderField("Content-Encoding");
        BufferedReader reader;
        if ("gzip".equals(contentEncoding)) {
            // 3. 手动用 GZIPInputStream 解压
            reader = new BufferedReader(new InputStreamReader(
                new GZIPInputStream(conn.getInputStream()), "UTF-8"));
        } else {
            // 无需解压,直接读取
            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
        }
        
        // 4. 读取解压后的内容
        String line;
        StringBuilder response = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            response.append(line);
        }
        reader.close();
        System.out.println("解压后的响应:" + response.toString());
    }
}

三、常见问题:客户端解压失败的原因

  1. 客户端未发送 Accept-Encoding 头:服务器会认为客户端不支持压缩,直接返回未压缩的响应,客户端无需解压(也不会有 Content-Encoding 头)。
  2. 响应体不是文本类数据:若服务器误将图片、视频等二进制数据压缩,客户端解压后会出现乱码(需在服务器配置中排除这些 MIME 类型)。
  3. 压缩算法不匹配:服务器用 Brotli(Content-Encoding: br)压缩,但客户端仅支持 Gzip,会导致解压失败(需客户端添加 Accept-Encoding: br 支持)。

总结

客户端解压的核心是 “基于 HTTP 头的自动协商”:

  • 浏览器 / 测试工具:完全自动,无需手动操作;
  • 代码客户端:主流框架(OkHttp、requests)默认自动解压,仅在不用框架时需手动处理;
  • 关键是确保客户端发送 Accept-Encoding 头,且支持服务器使用的压缩算法(Gzip 最通用,Brotli 需额外配置)。

只要服务器配置正确,客户端几乎无需额外开发,即可完成解压,实现 “压缩传输 - 自动解压” 的完整流程。

要不要我帮你整理一份 常见客户端解压配置清单?包含浏览器、Postman、Java/Python 代码客户端的具体操作步骤和验证方法,方便你快速核对客户端配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值