Springboot 之 Filter 实现超大响应 JSON 数据压缩

简介项目中,请求时发送超大 json 数据外;响应时也有可能返回超大 json数据 。上一篇实现了请求数据的 gzip 压缩 。本篇通过 filter 实现对响应 json 数据的压缩 。先了解一下以下两个概念:

  • 请求头:Accept-Encoding : gzip告诉服务器,该浏览器支持 gzip 压缩
  • 响应头:Content-Encoding : gzip告诉浏览器,输出信息使用了 gzip 进行压缩

Springboot 之 Filter 实现超大响应 JSON 数据压缩

文章插图
pom.xml 引入依赖<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.olive</groupId> <artifactId>response-compression</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>response-compression</name> <url>http://maven.apache.org</url> <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.14</version><relativePath /> <!-- lookup parent from repository --> </parent> <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target> </properties> <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.14</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.9.0</version></dependency> </dependencies></project>对Response进行包装GzipResponseWrapper 类重新定义了输出流,拦截需要输出的数据,直接缓存到 ByteArrayOutputStream 中 。
package com.olive.filter;import lombok.extern.slf4j.Slf4j;import javax.servlet.ServletOutputStream;import javax.servlet.WriteListener;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;import java.io.*;@Slf4jpublic class GzipResponseWrapper extends HttpServletResponseWrapper {/*** 字节数组缓冲流,用来保存截获到的输出数据*/private ByteArrayOutputStream buffer;/*** 重新定义servlet输出流,改变输出目的地将响应内容输出到给定的字节数组缓冲流中*/private GzipResponseWrapper.CustomServletOutputStream servletOutputStream;/*** 同上*/private PrintWriter writer;public GzipResponseWrapper(HttpServletResponse response) {super(response);//original HttpServletResponse objectbuffer = new ByteArrayOutputStream();servletOutputStream = new GzipResponseWrapper.CustomServletOutputStream(buffer);try {writer = new PrintWriter(new OutputStreamWriter(buffer, response.getCharacterEncoding()), true);} catch (UnsupportedEncodingException e) {log.error("GZipHttpServletResponse", e);}}@Overridepublic ServletOutputStream getOutputStream() throws IOException {return servletOutputStream;}@Overridepublic PrintWriter getWriter() throws IOException {return writer;}@Overridepublic void flushBuffer() throws IOException {if (servletOutputStream != null) {servletOutputStream.flush();}if (writer != null) {writer.flush();}}/*** 向外部提供一个获取截获数据的方法* @return 从response输出流中截获的响应数据*/public byte[] getOutputData() throws IOException {flushBuffer();return buffer.toByteArray();}private static class CustomServletOutputStream extends ServletOutputStream {/*** 字节数组缓冲流,用来保存截获到的输出数据*/private ByteArrayOutputStream buffer;public CustomServletOutputStream(ByteArrayOutputStream buffer) {this.buffer = buffer;}@Overridepublic boolean isReady() {return true;}@Overridepublic void setWriteListener(WriteListener listener) {}/*** 重写输出流相关的方法* 将输出数据写出到给定的ByteArrayOutputStream缓冲流中保存起来* @param b 输出的数据* @throws IOException*/@Overridepublic void write(int b) throws IOException {buffer.write(b);}}}

经验总结扩展阅读