您好,登錄后才能下訂單哦!
本篇內容主要講解“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壓縮、序列化及編碼轉義的用法”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。