How to Reduce API Payload Size with GZIP Compression in Spring Boot
Learn how to compress large JSON payloads in a Spring Boot advertising API using GZIP, implement a servlet filter and request wrapper to decompress on the server, and evaluate bandwidth, CPU trade‑offs, testing methods, and deployment steps to halve request size.
1. Business Background
The internal system of the company provides an advertisement saving interface that receives ad data from ADX. An example of the ad data structure is:
adName – the name of the advertisement
adTag – the HTML code of the advertisement, stored as a text field; the largest adTag can be as big as 60KB.
{
"adName":"",
"adTag":""
}Because the request payload can be very large, transmitting it directly brings several drawbacks:
It consumes network bandwidth, which may increase cloud costs.
Large payloads increase transmission time.
To address these issues, the senior team proposed the following idea:
Compress the JSON object string with GZIP before sending it to the advertisement saving interface. The request carries the compressed data, which greatly reduces the transmitted size. On the server side, decompress the data back to a JSON object before processing.
However, this approach also has drawbacks:
The request becomes more complex: the caller must compress the data, and the server must decompress it.
Additional CPU resources are required for compression and decompression.
It may affect existing interfaces.
Given the current business, the following solutions are proposed:
The internal system is I/O‑intensive, so using some CPU cycles to reduce network transmission is worthwhile.
Use a servlet filter to decompress the request body before it reaches the controller, keeping the controller logic unchanged (zero‑intrusion).
Distinguish whether decompression is needed by checking the Content-Encoding=gzip HTTP header.
Now the implementation plan.
2. Implementation Idea
Prerequisite Knowledge
HTTP request structure and the Content-Encoding header
GZIP compression method
Servlet Filter HttpServletRequestWrapper Spring Boot
Java I/O streams
Implementation flowchart:
Core Code
Create a Spring Boot project with a simple controller that receives a JSON object and returns it, simulating the ad‑saving operation.
/**
* @ClassName ProjectController
* @Author zhangjin
* @Date 2022/3/24 20:41
* @Description:
*/
@Slf4j
@RestController
public class AdvertisingController {
@PostMapping("/save")
public Advertising saveProject(@RequestBody Advertising advertising) {
log.info("获取内容" + advertising);
return advertising;
}
}
**
* @ClassName Project
* @Author zhangjin
* @Date 2022/3/24 20:42
* @Description:
*/
@Data
public class Advertising {
private String adName;
private String adTag;
}Write and register a GZIP filter.
/**
* @ClassName GZIPFilter
* @Author zhangjin
* @Date 2022/3/26 0:36
* @Description:
*/
@Slf4j
@Component
public class GZIPFilter implements Filter {
private static final String CONTENT_ENCODING = "Content-Encoding";
private static final String CONTENT_ENCODING_TYPE = "gzip";
@Override
public void init(FilterConfig filterConfig) throws ServletException {
log.info("init GZIPFilter");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long start = System.currentTimeMillis();
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String encodeType = httpServletRequest.getHeader(CONTENT_ENCODING);
if (CONTENT_ENCODING_TYPE.equals(encodeType)) {
log.info("请求:{} 需要解压", httpServletRequest.getRequestURI());
UnZIPRequestWrapper unZIPRequestWrapper = new UnZIPRequestWrapper(httpServletRequest);
filterChain.doFilter(unZIPRequestWrapper, servletResponse);
} else {
log.info("请求:{} 无需解压", httpServletRequest.getRequestURI());
filterChain.doFilter(servletRequest, servletResponse);
}
log.info("耗时:{}ms", System.currentTimeMillis() - start);
}
@Override
public void destroy() {
log.info("destroy GZIPFilter");
}
}
**
* @ClassName FilterRegistration
* @Author zhangjin
* @Date 2022/3/26 0:36
* @Description:
*/
@Configuration
public class FilterRegistration {
@Resource
private GZIPFilter gzipFilter;
@Bean
public FilterRegistrationBean<GZIPFilter> gzipFilterRegistrationBean() {
FilterRegistrationBean<GZIPFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(gzipFilter);
registration.setName("gzipFilter");
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}Implement UnZIPRequestWrapper to decompress the request body and rewrite it.
/**
* @ClassName UnZIPRequestWrapper
* @Author zhangjin
* @Date 2022/3/26 11:02
* @Description: JsonString compressed to binary -> decompress -> write back to body
*/
@Slf4j
public class UnZIPRequestWrapper extends HttpServletRequestWrapper {
private final byte[] bytes;
public UnZIPRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
try (BufferedInputStream bis = new BufferedInputStream(request.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
final byte[] body;
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) > 0) {
baos.write(buffer, 0, len);
}
body = baos.toByteArray();
if (body.length == 0) {
log.info("Body无内容,无需解压");
bytes = body;
return;
}
this.bytes = GZIPUtils.uncompressToByteArray(body);
} catch (IOException ex) {
log.info("解压缩步骤发生异常!");
ex.printStackTrace();
throw ex;
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
return new ServletInputStream() {
@Override
public boolean isFinished() { return false; }
@Override
public boolean isReady() { return false; }
@Override
public void setReadListener(ReadListener readListener) {}
@Override
public int read() throws IOException { return byteArrayInputStream.read(); }
};
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
}
}Utility class GZIPUtils for compression and decompression.
public class GZIPUtils {
public static final String GZIP_ENCODE_UTF_8 = "UTF-8";
/** string compress to GZIP byte array */
public static byte[] compress(String str) {
return compress(str, GZIP_ENCODE_UTF_8);
}
public static byte[] compress(String str, String encoding) {
if (str == null || str.length() == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
GZIPOutputStream gzip = new GZIPOutputStream(out);
gzip.write(str.getBytes(encoding));
gzip.close();
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
/** GZIP decompress */
public static byte[] uncompress(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try {
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
public static String uncompressToString(byte[] bytes) throws IOException {
return uncompressToString(bytes, GZIP_ENCODE_UTF_8);
}
public static String uncompressToString(byte[] bytes, String encoding) throws IOException {
byte[] result = uncompressToByteArray(bytes, encoding);
return new String(result);
}
public static byte[] uncompressToByteArray(byte[] bytes) throws IOException {
return uncompressToByteArray(bytes, GZIP_ENCODE_UTF_8);
}
public static byte[] uncompressToByteArray(byte[] bytes, String encoding) throws IOException {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try {
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
return out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
throw new IOException("解压缩失败!");
}
}
public static void saveFile(String filename, byte[] data) throws Exception {
if (data != null) {
String filepath = "/" + filename;
File file = new File(filepath);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(file);
fos.write(data, 0, data.length);
fos.flush();
fos.close();
System.out.println(file);
}
}
}3. Test Effect
Important pitfall: do not treat the compressed byte[] as a string for transmission, otherwise the size may become larger than the original. Two common ways to transmit compressed data are:
Base64‑encode the compressed byte[] and send it as a string (some compression loss, suitable for storing in Redis).
Write the compressed byte[] as a binary file and send the file in the request body (no compression loss).
Postman test for GZIP compressed request:
Set the request header to indicate the compression method.
Include the binary file containing the compressed byte[] in the request body.
Execute the request; the server correctly processes it and the request size is reduced by nearly half.
4. Demo Address
https://gitee.com/wx_1bceb446a4/gziptest
Thank you for reading, hope it helps :)
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
