SpringBoot进阶教程(八十七)数据压缩

最近在使用Redis的时候,经常遇到一些不常用的大key,对存储有一些负担。就想着把大key压缩一下。压缩可以分很多种,比如拆分JSON字符串​, ​​压缩JSON字符串​,优化JSON体积​,流式处理大型JSON​和分段存储。

v拆分JSON字符串​

1.1按结构拆分

数组拆分​​:若JSON包含大型数组,可将其拆分为多个小数组。

复制代码
// 示例:将大数组拆分为多个子数组
JSONArray bigArray = new JSONArray(jsonString);
int chunkSize = 100;
for (int i = 0; i < bigArray.length(); i += chunkSize) {
    JSONArray chunk = new JSONArray();
    for (int j = i; j < Math.min(i + chunkSize, bigArray.length()); j++) {
        chunk.put(bigArray.get(j));
    }
    String chunkJson = chunk.toString();
    // 处理或保存chunkJson
}

​​对象拆分​​:若JSON是嵌套对象,可按层级拆分为子对象。
1.2按大小拆分(流式处理)

使用流式API(如Jackson的JsonParser)逐块读取JSON内容,避免一次性加载到内存:

复制代码
JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(new File("large.json"))) {
    while (parser.nextToken() != null) {
        // 逐Token处理,如按特定条件拆分
    }
}

v压缩JSON字符串​

2.1使用GZIP压缩

复制代码
import java.util.zip.GZIPOutputStream;
import java.io.ByteArrayOutputStream;

public static byte[] compress(String data) throws IOException {
    ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length());
    try (GZIPOutputStream gzip = new GZIPOutputStream(bos)) {
        gzip.write(data.getBytes());
    }
    return bos.toByteArray();
}

// 压缩后的数据可用于传输或存储
byte[] compressed = compress(jsonString);

2.2使用Deflater压缩

复制代码
import java.util.zip.Deflater;

public static byte[] deflateCompress(String data) {
    Deflater deflater = new Deflater();
    deflater.setInput(data.getBytes());
    deflater.finish();
    byte[] buffer = new byte[1024];
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    while (!deflater.finished()) {
        int count = deflater.deflate(buffer);
        outputStream.write(buffer, 0, count);
    }
    deflater.end();
    return outputStream.toByteArray();
}

v优化JSON体积​​

3.1移除无用空格

使用紧凑格式(无缩进、换行):

复制代码
new JSONObject(jsonString).toString(); // 默认紧凑格式

3.2缩短键名

将长字段名替换为短名称:

复制代码
{"n":"Alice","a":30} // 原始键名可能为"name"、"age"

v流式处理大型JSON​​

使用流式API逐步解析,避免内存溢出:

复制代码
// Jackson流式API示例
JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(new File("large.json"))) {
    JsonToken token;
    while ((token = parser.nextToken()) != null) {
        if (token == JsonToken.START_ARRAY) {
            while (parser.nextToken() != JsonToken.END_ARRAY) {
                // 逐条处理数组元素
                JsonNode node = parser.readValueAsTree();
                // 处理node...
            }
        }
    }
}

v分页处理​

其实也是拆分,将数据拆成若干份

v实践方案

复制代码
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.apache.commons.codec.binary.Base64;
import java.nio.charset.StandardCharsets;

public class CompressHelper {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 方式1:去除JSON中的空格/换行等冗余字符(文本压缩)
     * @param formattedJson 格式化的JSON字符串(含空格换行)
     * @return 紧凑格式的JSON字符串
     * @throws IOException JSON解析异常
     */
    public static String compressJsonByRemovingSpaces(String formattedJson) throws IOException {
        JsonNode jsonNode = objectMapper.readTree(formattedJson);
        return objectMapper.writeValueAsString(jsonNode);
    }

    /**
     * 方式2:使用GZIP算法对JSON字符串进行二进制压缩(适合网络传输)
     * @param json 原始JSON字符串
     * @return Base64编码的压缩后字符串(可直接传输)
     * @throws IOException 压缩异常
     */
    public static String compressJsonByGzip(String json) throws IOException {
        try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
             GZIPOutputStream gzipOut = new GZIPOutputStream(byteOut)) {

            gzipOut.write(json.getBytes("UTF-8"));
            gzipOut.finish();

            return Base64.encodeBase64String(byteOut.toByteArray());
        }
    }

    public static String decompressJson(String source) throws IOException {
        byte[] compressedData = Base64.decodeBase64(source);
        try (ByteArrayInputStream byteIn = new ByteArrayInputStream(compressedData);
             GZIPInputStream gzipIn = new GZIPInputStream(byteIn);
             ByteArrayOutputStream byteOut = new ByteArrayOutputStream()) {

            // 读取压缩数据并解压缩
            byte[] buffer = new byte[1024];
            int len;
            while ((len = gzipIn.read(buffer)) != -1) {
                byteOut.write(buffer, 0, len);
            }
            return byteOut.toString(StandardCharsets.UTF_8.name());
        }
    }
}

v源码地址

https://github.com/toutouge/javademosecond/tree/master/hellolearn

作  者:请叫我头头哥

出  处:http://www.cnblogs.com/toutou/

关于作者:专注于基础平台的项目开发。如有问题或建议,请多多赐教!

版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

特此声明:所有评论和私信都会在第一时间回复。也欢迎园子的大大们指正错误,共同进步。或者直接私信

声援博主:如果您觉得文章对您有帮助,可以点击文章右下角**【推荐】** 一下。您的鼓励是作者坚持原创和持续写作的最大动力!

相关推荐
wxr06162 天前
部署Spring Boot项目+mysql并允许前端本地访问的步骤
前端·javascript·vue.js·阿里云·vue3·springboot
学编程的小鬼3 天前
SpringBoot日志
java·后端·springboot
小霞在敲代码5 天前
SpringBoot + RabbitMQ 消息队列案例
消息队列·springboot
老朋友此林5 天前
一文快速入门 MongoDB 、MongoDB 8.2 下载安装、增删改查操作、索引、SpringBoot整合 Spring Data MongoDB
数据库·mongodb·springboot
吹晚风吧5 天前
SSE是什么?SSE解决什么问题?在什么场景使用SSE?
java·springboot·sse
小霞在敲代码7 天前
SpringBoot连接PgSQL
springboot·pgsql
ccccczy_8 天前
Java微服务容器化与 Kubernetes 编排实战:从 Docker 多阶段构建到云原生弹性扩展
java·docker·kubernetes·springboot·microservices·cloudnative·containerization
王嘉俊9258 天前
Redis 入门:高效缓存与数据存储的利器
java·数据库·redis·后端·spring·缓存·springboot
王嘉俊9258 天前
Kafka 和 RabbitMQ 使用:消息队列的强大工具
java·分布式·中间件·kafka·消息队列·rabbitmq·springboot