响应压缩是 Web 应用一种常见的优化手段,通过压缩算法减小传输数据的体积,提高传输效率、节约带宽。客户端接收到数据后,使用相同的算法对数据进行解压从而获取到原始数据。
客户端和服务器需要通过 Header 来协商双方支持的压缩算法。
Accept-Encoding:请求头,告诉服务器客户端支持的压缩算法(多个使用逗号分割)。例如:Accept-Encoding: gzip, deflate。Content-Encoding:响应头,告诉客户端当前 Payload 使用的编码方式(压缩算法)。例如:Content-Encoding: gzip。常用的压缩算法如下:
JDK 提供了对 GZIP 压缩算法的实现:GZIPOutputStream 和 GZIPInputStream,我们可以用它们来实现 Gzip 压缩和解压缩。
在 Spring Boot 应用中创建一个 Controller,使用 GZIPOutputStream 把一张图片文件(20 KB)压缩后响应给客户端。
- package cn.springdoc.demo.web.controller;
-
- import java.io.IOException;
- import java.nio.file.Files;
- import java.nio.file.Path;
- import java.nio.file.Paths;
- import java.util.Optional;
- import java.util.zip.GZIPOutputStream;
-
- import org.springframework.http.HttpHeaders;
- import org.springframework.http.MediaType;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import jakarta.servlet.http.HttpServletRequest;
- import jakarta.servlet.http.HttpServletResponse;
-
-
- @RestController
- @RequestMapping("/demo")
- public class DemoController {
-
- @GetMapping
- public void file (HttpServletRequest request, HttpServletResponse response) throws IOException {
-
- // 20.0 KB 大小的图片文件
- Path file = Paths.get("C:\\Users\\KevinBlandy\\Desktop\\springdoc-logo.png");
-
- // 设置文件类型
- response.setContentType(Optional.ofNullable(Files.probeContentType(file)).orElse(MediaType.APPLICATION_OCTET_STREAM_VALUE));
-
- // 设置压缩方式为 gzip 【关键点 1:设置正确的 CONTENT_ENCODING 头】
- response.setHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
-
- // 包装 response 流为 gzip 流 【关键点 2:使用 GZIPOutputStream 封装 response 流,并写出数据】
- try(GZIPOutputStream gzipOutputStream = new GZIPOutputStream(response.getOutputStream())){
- // 响应给客户端
- Files.copy(file, gzipOutputStream);
- }
- }
- }
如上。关键点在于设置 CONTENT_ENCODING Header 为 gzip,告诉浏览器使用了 gzip 压缩算法,浏览器会自动使用相同算法进行解压缩。
最后,使用 GZIPOutputStream 封装 response 流,往 gzipOutputStream 中写入的数据就会被 gzip 压缩。
启动应用,使用浏览器访问:http://localhost:8080/demo:

通过控制台的网络面板,你可以看到:
Accept-Encoding 告诉服务器,它支持 gzip 压缩算法。gzip。图片在浏览器中预览成功,也说明服务器和客户端都进行了正确的编解码。
对于这种如此常用的功能,Spring Boot 早已提供了开箱即用的支持。
可以在 application.yaml / application.properties 文件中配置如下属性,开启全局 Gzip 响应压缩:
| 属性 | 说明 | 默认值 |
| server.compression.enabled | 是否开启全局响应压缩 | false |
| server.compression.excluded-user-agents | 以逗号分隔的 User Agent 列表,对这些 User Agent 的响应不会被压缩。 | |
| server.compression.mime-types | 逗号分割的文件 MIME Type(媒体类型),这些类型的文件才会被压缩。 | [text/html, text/xml, text/plain, text/css, text/javascript, application/javascript, application/json, application/xml] |
| server.compression.min-response-size | 进行压缩的最低 Content-Length 值。 | 2KB |
在 application.yaml 中添加如下配置:
- server:
- compression:
- # 开启响应压缩
- enabled: true
- mime-types:
- - image/png # 压缩 png 图片
- # 进行压缩的最小体积
- min-response-size: 1KB
其实只需要设置 server.compression.enabled=true 即可,这里故意设置 server.compression.min-response-size=1KB 完全是为了进行演示,因为示例图片不足 2KB。
server.compression.min-response-size值不应该过小,否则压缩后的数据体积可能比原始数据还大。
还需要覆盖 server.compression.mime-types 配置,因为默认配置的压缩的文件类型列表中不包含图片。
修改 Controller,如下:
- @GetMapping
- public ResponseEntity<Resource> file (HttpServletRequest request, HttpServletResponse response) throws IOException {
-
- // 20.0 KB 大小的图片文件
- Path file = Paths.get("C:\\Users\\KevinBlandy\\Desktop\\springdoc-logo.png");
-
- return ResponseEntity.ok()
- .contentType(MediaType.IMAGE_PNG) // 正确设置图片的 Content Type,浏览器才会预览图片
- .body(new InputStreamResource(Files.newInputStream(file)));
- }
这次不自己使用 GZIPOutputStream 进行压缩响应,而是直接返回 ResponseEntity 对象。这也是关键点,如果你想基于配置的全局 Gzip 响应压缩生效,则不能自己使用 HttpServletResponse 进行数据响应,必须要通过返回对象,由 DispatcherServlet 处理,全局响应压缩才会生效。
重启应用,用浏览器再次请求 http://localhost:8080/demo,你会发现结果跟上节中的测试结果一样。全局 Gzip 压缩配置生效。