Spring Boot 与 Gzip 压缩

响应压缩是 Web 应用一种常见的优化手段,通过压缩算法减小传输数据的体积,提高传输效率、节约带宽。客户端接收到数据后,使用相同的算法对数据进行解压从而获取到原始数据。

客户端和服务器需要通过 Header 来协商双方支持的压缩算法。

  • Accept-Encoding:请求头,告诉服务器客户端支持的压缩算法(多个使用逗号分割)。例如:Accept-Encoding: gzip, deflate
  • Content-Encoding:响应头,告诉客户端当前 Payload 使用的编码方式(压缩算法)。例如:Content-Encoding: gzip

常用的压缩算法如下:

  • gzip
  • deflate
  • br

JDK 提供了对 GZIP 压缩算法的实现:GZIPOutputStreamGZIPInputStream,我们可以用它们来实现 Gzip 压缩和解压缩。

一、使用 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

通过控制台的网络面板,你可以看到:

  1. 浏览器通过 Accept-Encoding 告诉服务器,它支持 gzip 压缩算法。
  2. 服务器正确地指定了 Payload 的编码类型为 gzip
  3. 由于使用了 Gzip 压缩,数据的传输体积小于文件体积。

图片在浏览器中预览成功,也说明服务器和客户端都进行了正确的编解码。

二、Spring Boot 配置响应压缩

对于这种如此常用的功能,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<Resource> 对象。这也是关键点,如果你想基于配置的全局 Gzip 响应压缩生效,则不能自己使用 HttpServletResponse 进行数据响应 ,必须要通过返回对象,由 DispatcherServlet 处理,全局响应压缩才会生效。

重启应用,用浏览器再次请求 http://localhost:8080/demo,你会发现结果跟上节中的测试结果一样。全局 Gzip 压缩配置生效。

相关推荐
程序员张32 小时前
Maven编译和打包插件
java·spring boot·maven
ybq195133454313 小时前
Redis-主从复制-分布式系统
java·数据库·redis
weixin_472339464 小时前
高效处理大体积Excel文件的Java技术方案解析
java·开发语言·excel
灵犀学长4 小时前
EasyExcel之SheetWriteHandler:解锁Excel写入的高阶玩法
spring boot·excel
小毛驴8504 小时前
Linux 后台启动java jar 程序 nohup java -jar
java·linux·jar
zwjapple4 小时前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
DKPT5 小时前
Java桥接模式实现方式与测试方法
java·笔记·学习·设计模式·桥接模式
好奇的菜鸟6 小时前
如何在IntelliJ IDEA中设置数据库连接全局共享
java·数据库·intellij-idea
tan180°6 小时前
MySQL表的操作(3)
linux·数据库·c++·vscode·后端·mysql
DuelCode7 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis