温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

java压缩、序列化及编码转义的用法

发布时间:2021-06-28 14:50:14 来源:亿速云 阅读:254 作者:chen 栏目:大数据

本篇内容主要讲解“java压缩、序列化及编码转义的用法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java压缩、序列化及编码转义的用法”吧!

1、服务端编码时,压缩报文、编码转义、摘要信息等都是常见场景,本次针对对应相关工具进行分类

2、压缩报文:GZIP

    Gzip是常见的压缩报文算法工具,默认使用DEFLATE实现,此为LZ77+hufman的结合体。即先通过LZ77进行相同字符归类压缩在使用哈夫曼编码整理成树,完成压缩。PS:LZ77算法和hufman算法可参见:https://www.cnblogs.com/kuang17/p/7193124.html

       LZ77算法可概述为扫描整个报文,找出相同字符串并使用前面的代替后面的。这样通过字符串和位移及长度实现初步压缩,可见,重复率越高压缩比越大。

       hufman算法可概述为扫描整个报文,重复率越少的字符,离根节点越远,这样越是离根节点近,重复率就越高,形成的hufman同样遵循上述原则:即重复率越高压缩比越大

      附java代码:    

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * @author ysma 2019-10-08
 */
@Slf4j
public class GZipUtil {

    private static final String EN_CODING = "utf-8";

    /**压缩*/
    public static String compress(String dataMsg){

        try {
            //1.base64编码 二进制转义
            byte[] base64Str = Base64.getEncoder().encode(dataMsg.getBytes(EN_CODING));

            //2.zip压缩
            ByteArrayOutputStream baOs = new ByteArrayOutputStream();
            GZIPOutputStream zipOs = new GZIPOutputStream(baOs, true);
            zipOs.write(base64Str);
            zipOs.flush();
            zipOs.close();//关闭流 输出缓存区内容

            //3.获取并转义压缩内容
            return Base64.getEncoder().encodeToString(baOs.toByteArray());
        } catch (IOException e) {
            log.error("GZipUtil.compress 压缩异常", e);
            throw new RuntimeException("GZipUtil.compress 压缩异常", e);
        }
    }

    /**解压缩*/
    public static String unCompress(String dataMsg){

        //1.转义二进制
        byte[] base64Bytes = Base64.getDecoder().decode(dataMsg.getBytes());

        try {
            //1.zip解压缩
            ByteArrayOutputStream baOs = new ByteArrayOutputStream();
            ByteArrayInputStream baIs = new ByteArrayInputStream(base64Bytes);
            GZIPInputStream zipIs = new GZIPInputStream(baIs);

            byte[] temp = new byte[256];
            /*while (zipIs.read(temp) >=0){
                baOs.write(temp);
                1.当报文较大时,会存在冗余读,导致解压后出现冗余内容!
                2.原因在于倒数第二部分内容 为较长字符串时,内容超长
                3.当解析最后一部分内容时 由于内容较短,只覆盖了前N个长度的内容, 但是write是写了全部的
            }*/
            int n;
            while ((n = zipIs.read(temp)) >= 0){
                baOs.write(temp, 0, n);
            }

            //3.base64 二进制恢复
            byte[] originMsg = Base64.getDecoder().decode(baOs.toByteArray());
            return new String(originMsg);
        } catch (IOException e) {
            log.error("GZipUtil.unCompress 解压缩异常", e);
            throw new RuntimeException("GZipUtil.unCompress 解压缩异常", e);
        }
    }

    public static void main(String[] args) {
        String a ="{\"extendDsList\":[{\"datasource\":{\"code\":\"first_internal_1\",\"createTime\":\"2019-10-09 14:58:21\",\"description\":\"行内第一数据源\",\"modifyTime\":\"2019-10-09 16:55:25\",\"name\":\"行内第一数据源\",\"providerCode\":\"internal_database\",\"providerInfo\":\"{\\\"jdbcUrl\\\":\\\"jdbc:mysql://10.2.1.106:3306/data_engine\\\",\\\"password\\\":\\\"Y1R1MTIzNDU2\\\",\\\"userName\\\":\\\"ctu\\\"}\",\"providerName\":\"数据聚合联调库\",\"providerType\":\"MYSQL\",\"querySql\":\"select code, name, url, source from name_list_info where code = #{code}\",\"serviceTtl\":7,\"serviceUrl\":\"\",\"status\":1,\"type\":4},\"paramReqList\":[{\"code\":\"name\",\"createTime\":\"2019-10-09 14:58:21\",\"dataType\":\"string\",\"modifyTime\":\"2019-10-09 15:01:30\",\"name\":\"名称\",\"required\":1},{\"code\":\"id\",\"createTime\":\"2019-10-09 14:58:21\",\"dataType\":\"long\",\"modifyTime\":\"2019-10-09 15:01:34\",\"name\":\"身份证号\",\"required\":1}],\"paramResList\":[{\"code\":\"phone\",\"createTime\":\"2019-10-09 14:58:21\",\"dataType\":\"int\",\"modifyTime\":\"2019-10-09 15:01:24\",\"name\":\"手机号\"},{\"code\":\"rank\",\"createTime\":\"2019-10-09 14:58:21\",\"dataType\":\"string\",\"modifyTime\":\"2019-10-09 15:01:19\",\"name\":\"排位分\"}],\"provider\":{\"code\":\"internal_database\",\"createTime\":\"2019-10-09 14:58:09\",\"modifyTime\":\"2019-10-09 14:58:09\",\"name\":\"数据聚合联调库\"}}],\"realDsList\":[]}";
        System.out.println(GZipUtil.unCompress(GZipUtil.compress(a)));
    }
}

3、base64编码可谓是最常见的加解密算法,如上GzipUtil工具类中就夹杂了base64编码相关内容:一种基于64个可打印字符[通过52个大小写英文字母、10个数字和+\两个符号进行]来表示二进制数据的方法。其不可读性的特点通常用来进行简单的加密操作,同时其二进制ASCII码的特点也经常用来平衡GBK、UTF8等编码造成的不一致,如邮件应用。

    Base64编码也有变种如UrlEnCode等js编码。最简单应用可使用java.util.Base64 工具包

4、信息摘要MD5,最新研究表明Md5已不是不可破解的算法。 但是其作为复杂算法的摘要功能和压缩功能仍然具有广阔的应用场景。毕竟解码一段Md5还是很复杂的,如果你的内容不是国家级机密,我相信没有人会耗尽心机去破解那段"毫无意义"的内容。

    附代码[32位和16位摘要]:    

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;

/**
 * @author ysma md5工具
 */
@Slf4j
public class Md5Util {

    public static void main(String[] args) {
        String a = "{\"ysma\":\"dx\",\"hello\":\"world\"}";
        String b = hexBit32(a);
        System.out.println(b);
        String c = hexBit16(b);
        System.out.println(c);
    }

    public static byte[] digest(String data){
        try {
            // 拿到一个MD5转换器
            MessageDigest messageDigest =MessageDigest.getInstance("MD5");

            // 输入的字符串转换成字节数组
            byte[] inputByteArray = data.getBytes();

            // inputByteArray是输入字符串转换得到的字节数组
            messageDigest.update(inputByteArray);

            // 转换并返回结果,也是字节数组,包含16个元素
            return messageDigest.digest();
        } catch (NoSuchAlgorithmException e) {
            log.error("Md5Util.stringMD5 exception", e);
            return null;
        }
    }

    public static String hexBit32(String data) {
        // 字符数组转换成字符串返回
        return Hex.encodeHexString(Objects.requireNonNull(digest(data)), false);
    }

    public static String hexBit16(String data) {
        // 字符数组转换成字符串返回
        return hexBit32(data).substring(8, 24);
    }
}

5、序列化kryo:Kryo是一种快速高效的Java对象图(Object graph)序列化框架。

    java的序列化是通过ObjectOutPutStream实现的,较慢。kryo因为指定注册了序列化的对象,速度会快,经测试为java版的1/5。

        代码收集:

import com.esotericsoftware.kryo.Kryo;
 
public class KryoSingleton{
    private KryoSingleton(){}
    public static Kryo getInstance(){
        return Singleton.INSTANCE.getInstance();
    }
    
    private static enum Singleton{
        INSTANCE;
        
        private Kryo singleton;
        //JVM会保证此方法绝对只调用一次
        private Singleton(){
            singleton = new Kryo();
            // 如果没有多层次的对象引用相互引用,设为false,提高性能
            singleton.setReferences(false);
            // 注册序列化/反序列化的类
            singleton.register(SceneLayout.class);
            singleton.register(SceneLayoutMethod.class);
            
        }
        public Kryo getInstance(){
            return singleton;
        }
    }
}



--------------------------------------------------------------------------------
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
 
import org.apache.commons.codec.binary.Base64;
import org.objenesis.strategy.StdInstantiatorStrategy;
 
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
 
/**
 * Kryo Utils
 * <p/>
 */
public class KryoUtil {
 
    private static final String DEFAULT_ENCODING = "UTF-8";
 
    //每个线程的 Kryo 实例
    private static final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {
        @Override
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
            /**
             * 不要轻易改变这里的配置!更改之后,序列化的格式就会发生变化,
             * 上线的同时就必须清除 Redis 里的所有缓存,
             * 否则那些缓存再回来反序列化的时候,就会报错
             */
            //支持对象循环引用(否则会栈溢出)
            kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置
 
            //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册)
            kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置
 
            //Fix the NPE bug when deserializing Collections.
            ((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
                    .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());
 
            return kryo;
        }
    };
 
    /**
     * 获得当前线程的 Kryo 实例
     *
     * @return 当前线程的 Kryo 实例
     */
    public static Kryo getInstance() {
        return kryoLocal.get();
    }
 
    //-----------------------------------------------
    //          序列化/反序列化对象,及类型信息
    //          序列化的结果里,包含类型的信息
    //          反序列化时不再需要提供类型
    //          (推荐使用下面一组方法)
    //-----------------------------------------------
 
    /**
     * 将对象【及类型】序列化为字节数组
     * 
     * @param obj 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字节数组
     */
    public static <T> byte[] writeToByteArray(T obj) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Output output = new Output(byteArrayOutputStream);
 
        Kryo kryo = getInstance();
        kryo.writeClassAndObject(output, obj);
        output.flush();
 
        return byteArrayOutputStream.toByteArray();
    }
 
    /**
     * 将对象【及类型】序列化为 String
     * 利用了 Base64 编码
     *
     * @param obj 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字符串
     */
    public static <T> String writeToString(T obj) {
        try {
            return new String(Base64.encodeBase64(writeToByteArray(obj)), DEFAULT_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
 
    /**
     * 将字节数组反序列化为原对象
     *
     * @param byteArray writeToByteArray 方法序列化后的字节数组
     * @param <T>       原对象的类型
     * @return 原对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T readFromByteArray(byte[] byteArray) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
        Input input = new Input(byteArrayInputStream);
 
        Kryo kryo = getInstance();
        return (T) kryo.readClassAndObject(input);
    }
 
    /**
     * 将 String 反序列化为原对象
     * 利用了 Base64 编码
     *
     * @param str writeToString 方法序列化后的字符串
     * @param <T> 原对象的类型
     * @return 原对象
     */
    public static <T> T readFromString(String str) {
        try {
            return readFromByteArray(Base64.decodeBase64(str.getBytes(DEFAULT_ENCODING)));
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
 
    //-----------------------------------------------
    //         下面一组方法与上面的一组的区别在于 只序列化/反序列化对象
    //          序列化的结果里,不包含类型的信息
    //-----------------------------------------------
 
    /**
     * 将对象序列化为字节数组  
     * @param obj 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字节数组
     */
    public static <T> byte[] writeObjectToByteArray(T obj) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Output output = new Output(byteArrayOutputStream);
 
        Kryo kryo = getInstance();
        kryo.writeObject(output, obj);
        output.flush();
 
        return byteArrayOutputStream.toByteArray();
    }
 
    /**
     * 将对象序列化为 String
     * 利用了 Base64 编码
     *
     * @param obj 任意对象
     * @param <T> 对象的类型
     * @return 序列化后的字符串
     */
    public static <T> String writeObjectToString(T obj) {
        try {
            return new String(Base64.encodeBase64(writeObjectToByteArray(obj)), DEFAULT_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
 
    /**
     * 将字节数组反序列化为原对象
     *
     * @param byteArray writeToByteArray 方法序列化后的字节数组
     * @param clazz     原对象的 Class
     * @param <T>       原对象的类型
     * @return 原对象
     */
    public static <T> T readObjectFromByteArray(byte[] byteArray, Class<T> clazz) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
        Input input = new Input(byteArrayInputStream);
 
        Kryo kryo = getInstance();
        return kryo.readObject(input, clazz);
    }
 
    /**
     * 将 String 反序列化为原对象
     * 利用了 Base64 编码
     *
     * @param str   writeToString 方法序列化后的字符串
     * @param clazz 原对象的 Class
     * @param <T>   原对象的类型
     * @return 原对象
     */
    public static <T> T readObjectFromString(String str, Class<T> clazz) {
        try {
            return readObjectFromByteArray(Base64.decodeBase64(str.getBytes(DEFAULT_ENCODING)), clazz);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
}
=================

=================分割线==============================

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.etonenet.esms.Mt;

public class KryoUtil {
  public static final int KRYO_MSISDN_FILTER_LIST = 10;
	public static final int KRYO_MATCH = 11;

	// Kryo instance is not threadsafe, but expensive, so that is why it is
	// placed in a ThreadLocal.
	public static final ThreadLocal<Kryo> KRYO_THREAD_LOCAL = new ThreadLocal<Kryo>() {
		@Override
		protected Kryo initialValue() {
			Kryo kryo = new Kryo();
			kryo.register(Mt.class);
			return kryo;
		}
	};

	public static <T> byte[] out(T object) {
		Kryo kryo = KRYO_THREAD_LOCAL.get();
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		Output output = new Output(byteArrayOutputStream);
		kryo.writeObject(output, object);
		output.flush();
		output.close();
		return byteArrayOutputStream.toByteArray();
	}

	public static <T> T in(byte[] bytes, Class<T> clazz) {
		Kryo kryo = KRYO_THREAD_LOCAL.get();
		Input input = new Input(new ByteArrayInputStream(bytes));
		return kryo.readObject(input, clazz);
	}

}

    参见:https://www.cnblogs.com/benwu/articles/4826268.html

            https://blog.csdn.net/lzj1005642974/article/details/77991408

            https://blog.csdn.net/fuwenshen/article/details/98485865

到此,相信大家对“java压缩、序列化及编码转义的用法”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI