一溅固、背景
最近剛剛做完一個中文漢字筆畫排序的功能付秕,鏈接如下:
其中優(yōu)化之后,將數(shù)據(jù)庫的內(nèi)容励幼,序列化成為了json數(shù)據(jù)汰寓,然后通過解析json數(shù)據(jù),拿到漢字筆畫的相關(guān)信息苹粟。但是未處理前的json文件有滑,體積較大,有2.13Mb嵌削,因此需要壓縮才行毛好。
部分數(shù)據(jù)如下所示:
{
"33828": {
"code": "33828",
"name": "螢",
"order": "7298",
"strokeSum": "11"
},
"22920": {
"code": "22920",
"name": "媽",
"order": "1051",
"strokeSum": "6"
},
"20718": {
"code": "20718",
"name": "僮",
"order": "13341",
"strokeSum": "14"
},
"30615": {
"code": "30615",
"name": "瞗",
"order": "15845",
"strokeSum": "16"
},
"36969": {
"code": "36969",
"name": "適",
"order": "13506",
"strokeSum": "14"
}
}
二、常規(guī)壓縮json
2.1 未處理前的json文件
未處理前的json文件苛秕,格式好看但是體積較大肌访。
未處理前的json文件,一共占用125414行
未處理的原始json文件大小為2.13Mb
2.2 將JSON壓縮成一行艇劫,去掉換行和空格字符
在Android Studio中打開吼驶,如下所示:
將JSON壓縮成一行,去掉換行和空格字符后的json文件大小為:1.39Mb,只之前的2.13Mb小了整整0.74Mb蟹演,這個在移動端是很可觀的優(yōu)化风钻!
2.3 將JSON的key進行縮短
json 是 key-value 結(jié)構(gòu),如果定義好規(guī)范酒请,則可以將 key 盡量縮短骡技,甚至是無意義的字母,但前提是文檔一定要寫清楚羞反,避免不必要的麻煩布朦。
比如之前的 key-value結(jié)構(gòu)如下所示:
{
"33828": {
"code": "33828",
"name": "螢",
"order": "7298",
"strokeSum": "11"
},
"22920": {
"code": "22920",
"name": "媽",
"order": "1051",
"strokeSum": "6"
},
"20718": {
"code": "20718",
"name": "僮",
"order": "13341",
"strokeSum": "14"
},
"30615": {
"code": "30615",
"name": "瞗",
"order": "15845",
"strokeSum": "16"
},
"36969": {
"code": "36969",
"name": "適",
"order": "13506",
"strokeSum": "14"
}
}
現(xiàn)在我們將key進行優(yōu)化,使用
c 代替 code
n 代替 name
o 代替 order
s 代替 strokeSum
將JSON的key進行縮短優(yōu)化后的json文件大小為:1.77Mb昼窗,只之前的2.13Mb小了整整0.36Mb是趴,這個在移動端是很可觀的優(yōu)化!
然后再將縮短key之后的文件澄惊,重復(fù)【2.2 將JSON壓縮成一行右遭,去掉換行和空格字符】的操作。
再看一看文件大小為1.04Mb缤削,比最開始的原始數(shù)據(jù)2.13Mb小了整整1.09Mb,這個在移動端是很可觀的優(yōu)化吹榴!
當(dāng)然這樣key的名字變化了亭敢,對應(yīng)解析Json的java實體bean也要修改一下。
因為我使用的是jackson來進行json解析的图筹,所以使用注解@JsonProperty來表示一下修改的json文件對應(yīng)原來的java bean里面的屬性帅刀,這樣解析的時候就不會出錯了。
2.4 常規(guī)總結(jié)
經(jīng)過上面的常規(guī)操作远剩,
我們的json文件大小減少到了1.04Mb扣溺,
比最開始的原始數(shù)據(jù)2.13Mb,
小了整整1.09Mb瓜晤,
壓縮率為51.174%锥余,壓縮后體積為原來的48.826%
已經(jīng)算很給力了,但是這個json文件還是有1.04Mb啊痢掠,是否還可以進行壓縮呢驱犹?答案是肯定的,我們下面介紹下使用算法對該json文件進行壓縮足画。
三雄驹、使用壓縮算法進行壓縮
3.1 使用Deflater壓縮json,Inflater解壓json
Deflater 是同時使用了LZ77算法與哈夫曼編碼的一個無損數(shù)據(jù)壓縮算法淹辞。
我們可以使用 java 提供的 Deflater 和 Inflater 類對 json 進行壓縮和解壓縮医舆,下面是工具類
package com.oyp.sort.utils;
import android.support.annotation.Nullable;
import android.util.Base64;
import java.io.ByteArrayOutputStream;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
/**
* DeflaterUtils 壓縮字符串
*/
public class DeflaterUtils {
/**
* 壓縮
*/
public static String zipString(String unzipString) {
/**
* https://www.yiibai.com/javazip/javazip_deflater.html#article-start
* 0 ~ 9 壓縮等級 低到高
* public static final int BEST_COMPRESSION = 9; 最佳壓縮的壓縮級別。
* public static final int BEST_SPEED = 1; 壓縮級別最快的壓縮。
* public static final int DEFAULT_COMPRESSION = -1; 默認壓縮級別蔬将。
* public static final int DEFAULT_STRATEGY = 0; 默認壓縮策略爷速。
* public static final int DEFLATED = 8; 壓縮算法的壓縮方法(目前唯一支持的壓縮方法)。
* public static final int FILTERED = 1; 壓縮策略最適用于大部分數(shù)值較小且數(shù)據(jù)分布隨機分布的數(shù)據(jù)娃胆。
* public static final int FULL_FLUSH = 3; 壓縮刷新模式遍希,用于清除所有待處理的輸出并重置拆卸器。
* public static final int HUFFMAN_ONLY = 2; 僅用于霍夫曼編碼的壓縮策略里烦。
* public static final int NO_COMPRESSION = 0; 不壓縮的壓縮級別凿蒜。
* public static final int NO_FLUSH = 0; 用于實現(xiàn)最佳壓縮結(jié)果的壓縮刷新模式。
* public static final int SYNC_FLUSH = 2; 用于清除所有未決輸出的壓縮刷新模式; 可能會降低某些壓縮算法的壓縮率胁黑。
*/
//使用指定的壓縮級別創(chuàng)建一個新的壓縮器废封。
Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
//設(shè)置壓縮輸入數(shù)據(jù)。
deflater.setInput(unzipString.getBytes());
//當(dāng)被調(diào)用時丧蘸,表示壓縮應(yīng)該以輸入緩沖區(qū)的當(dāng)前內(nèi)容結(jié)束漂洋。
deflater.finish();
final byte[] bytes = new byte[256];
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);
while (!deflater.finished()) {
//壓縮輸入數(shù)據(jù)并用壓縮數(shù)據(jù)填充指定的緩沖區(qū)。
int length = deflater.deflate(bytes);
outputStream.write(bytes, 0, length);
}
//關(guān)閉壓縮器并丟棄任何未處理的輸入力喷。
deflater.end();
return Base64.encodeToString(outputStream.toByteArray(), Base64.NO_PADDING);
}
/**
* 解壓縮
*/
@Nullable
public static String unzipString(String zipString) {
byte[] decode = Base64.decode(zipString, Base64.NO_PADDING);
//創(chuàng)建一個新的解壓縮器 https://www.yiibai.com/javazip/javazip_inflater.html
Inflater inflater = new Inflater();
//設(shè)置解壓縮的輸入數(shù)據(jù)刽漂。
inflater.setInput(decode);
final byte[] bytes = new byte[256];
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);
try {
//finished() 如果已到達壓縮數(shù)據(jù)流的末尾,則返回true弟孟。
while (!inflater.finished()) {
//將字節(jié)解壓縮到指定的緩沖區(qū)中贝咙。
int length = inflater.inflate(bytes);
outputStream.write(bytes, 0, length);
}
} catch (DataFormatException e) {
e.printStackTrace();
return null;
} finally {
//關(guān)閉解壓縮器并丟棄任何未處理的輸入。
inflater.end();
}
return outputStream.toString();
}
}
3.1.1 壓縮原始的stroke.json數(shù)據(jù)
然后我們先將原始的stroke.json數(shù)據(jù)壓縮成deFlaterStrokeJson.json拂募。
//原始文件 stroke.json
String strokeJson = LocalFileUtils.getStringFormAsset(context, "stroke.json");
mapper = JSONUtil.toCollection(strokeJson, HashMap.class, String.class, Stroke.class);
// 使用 Deflater 加密
String deFlaterStrokeJson = DeflaterUtils.zipString(strokeJson);
writeFile(deFlaterStrokeJson,"deFlaterStrokeJson.json");
其中 writeFile方法是寫入到sdcard的方法庭猩。
private static void writeFile(String mapperJson, String fileName) {
Writer write = null;
try {
File file = new File(Environment.getExternalStorageDirectory(), fileName);
Log.d(TAG, "file.exists():" + file.exists() + " file.getAbsolutePath():" + file.getAbsolutePath());
// 如果父目錄不存在,創(chuàng)建父目錄
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
// 如果已存在,刪除舊文件
if (file.exists()) {
file.delete();
}
file.createNewFile();
// 將格式化后的字符串寫入文件
write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
write.write(mapperJson);
write.flush();
write.close();
} catch (Exception e) {
Log.e(TAG, "e = " + Log.getStackTraceString(e));
}finally {
if (write != null){
try {
write.close();
} catch (IOException e) {
Log.e(TAG, "e = " + Log.getStackTraceString(e));
}
}
}
}
運行完畢之后陈症,將sdcard中的deFlaterStrokeJson.json導(dǎo)出來蔼水,放到assets目錄下,以備后續(xù)解析使用录肯。
使用Deflater壓縮json趴腋,壓縮后大小為 387KB,比上一次的1067KB论咏,又少了很多很多于样。
經(jīng)過Deflater壓縮和Base64編碼之后的deFlaterStrokeJson.json文件,如下所示:
3.1.2 還原成原始的stroke.json數(shù)據(jù)
關(guān)壓縮還不行潘靖,我們得使用壓縮后的json文件數(shù)據(jù)啊穿剖,因此我們還需要將壓縮后的json數(shù)據(jù)進行解壓,操作如下所示:
//使用 Inflater 解密
String deFlaterStrokeJson = LocalFileUtils.getStringFormAsset(context, "deFlaterStrokeJson.json");
String strokeJson = DeflaterUtils.unzipString(deFlaterStrokeJson);
mapper = JSONUtil.toCollection(strokeJson, HashMap.class, String.class, Stroke.class);
解壓之后運行一切正常卦溢!完美糊余!
3.1.3 Deflater壓縮總結(jié)
經(jīng)過上面的常規(guī)操作秀又,
我們的json文件大小減少到了387KB,
比剛才未使用壓縮算法的原始數(shù)據(jù)1067KB贬芥,
小了整整680KB吐辙,
壓縮率為63.73%,壓縮后體積為原來的36.27%
優(yōu)化步驟 | 體積 |
---|---|
1.未處理的原始json | 2.13MB |
2.將JSON壓縮成一行蘸劈,去掉換行和空格字符 | 1.39MB |
3.將JSON的key進行縮短 | 1.04MB |
4.使用Deflater壓縮json,Base64編碼 | 0.38MB |
3.2 使用Gzip壓縮解壓json
在我封裝的http庫里面昏苏,有對請求json數(shù)據(jù)進行Gzip壓縮,對服務(wù)器返回的json數(shù)據(jù)進行Gzip解壓威沫。這里也來試一下Gzip壓縮json贤惯。
編寫一個 Gzip壓縮解壓并使用Base64進行編碼工具類
package com.oyp.sort.utils;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* Gzip壓縮解壓并使用Base64進行編碼工具類
*/
public class GzipUtil {
private static final String TAG = "GzipUtil";
/**
* 將字符串進行g(shù)zip壓縮
*
* @param data
* @param encoding
* @return
*/
public static String compress(String data, String encoding) {
if (data == null || data.length() == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip;
try {
gzip = new GZIPOutputStream(out);
gzip.write(data.getBytes(encoding));
gzip.close();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.encodeToString(out.toByteArray(), Base64.NO_PADDING);
}
public static String uncompress(String data, String encoding) {
if (TextUtils.isEmpty(data)) {
return null;
}
byte[] decode = Base64.decode(data, Base64.NO_PADDING);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(decode);
GZIPInputStream gzipStream = null;
try {
gzipStream = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = gzipStream.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
Log.e(TAG, "e = " + Log.getStackTraceString(e));
} finally {
try {
out.close();
if (gzipStream != null) {
gzipStream.close();
}
} catch (IOException e) {
Log.e(TAG, "e = " + Log.getStackTraceString(e));
}
}
return new String(out.toByteArray(), Charset.forName(encoding));
}
}
3.2.1 壓縮原始的stroke.json數(shù)據(jù)
//原始文件 stroke.json
String strokeJson = LocalFileUtils.getStringFormAsset(context, "stroke.json");
mapper = JSONUtil.toCollection(strokeJson, HashMap.class, String.class, Stroke.class);
// 使用 GZIP 壓縮
String gzipStrokeJson = GzipUtil.compress(strokeJson,CHARSET_NAME);
writeFile(gzipStrokeJson,"gzipStrokeJson.json");
運行完畢之后,將sdcard中的gzipStrokeJson.json導(dǎo)出來棒掠,放到assets目錄下孵构,以備后續(xù)解析使用。
導(dǎo)出來的gzipStrokeJson.json文件為405kb烟很,沒有比剛才使用Deflater壓縮json后大小為 387KB優(yōu)秀颈墅!
3.2.2 還原成原始的stroke.json數(shù)據(jù)
關(guān)壓縮還不行,我們得使用壓縮后的json文件數(shù)據(jù)啊雾袱,因此我們還需要將壓縮后的json數(shù)據(jù)進行解壓恤筛,操作如下所示:
//使用 GZIP 解壓
String gzipStrokeJson = LocalFileUtils.getStringFormAsset(context, "gzipStrokeJson.json");
String strokeJson = GzipUtil.uncompress(gzipStrokeJson,CHARSET_NAME);
mapper = JSONUtil.toCollection(strokeJson, HashMap.class, String.class, Stroke.class);
解壓之后,json解析一切正常芹橡!
3.2.3 Gzip壓縮總結(jié)
經(jīng)過上面的常規(guī)操作叹俏,
我們的json文件大小減少到了405kb,
雖然比不上剛才的Deflater壓縮:387KB僻族,
但是比剛才未使用壓縮算法的原始數(shù)據(jù)1067KB,
小了整整662KB屡谐,
壓縮率為62.04%述么,壓縮后體積為原來的37.95%,也是不錯的愕掏!
四度秘、 其他壓縮算法
除了上面的算法之外,我們還可以使用很多其他的壓縮算法饵撑,進一步壓縮json的體積剑梳。我們的原始json中還是有很多重復(fù)的key值可以進行優(yōu)化的,下面的算法中有部分可以進行key優(yōu)化滑潘!
常見的json壓縮算法有CJSON與HPack垢乙,其原理都是將key和value進行抽離,節(jié)省掉部分的重復(fù)的key值造成的空間消耗语卤。
4.1 CJSON
CJSON 的壓縮算法, 主要是將資料抽離成 Template 與 Value,節(jié)省掉重復(fù)的 "Key 值".
原始JSON:
[{
"x": 100,
"y": 100
},
{
"x": 100,
"y": 100,
"width": 200,
"height": 150
},
{}
]
CJSON壓縮后:
{
"templates": [
[0, "x", "y"],
[1, "width", "height"]
],
"values": [{
"values": [1, 100, 100]
},
{
"values": [2, 100, 100, 200, 150]
},
{}
]
}
4.2 HPack
HPack 的壓縮算法, 也是將 Key, Value 抽離, 陣列中第一個值, 就是 HPack 的 Template, 后面依序就是 Value.
[
{
"name": "Andrea",
"age": 31,
"gender": "Male",
"skilled": true
},
{
"name": "Eva",
"age": 27,
"gender": "Female",
"skilled": true
},
{
"name": "Daniele",
"age": 26,
"gender": "Male",
"skilled": false
}
]
壓縮之后的數(shù)據(jù)
[
[
"name",
"age",
"gender",
"skilled"
],
[
"Andrea",
31,
"Male",
true
],
[
"Eva",
27,
"Female",
true
],
[
"Daniele",
26,
"Male",
false
]
]
兩種方法都是主要講json 的 鍵抽出來統(tǒng)一建成索引追逮,只是最后的格式不同酪刀。
HPack 簡化后的格式比CJSON 少了許多字符,所以HPack 的壓縮效率比較高钮孵。數(shù)據(jù)量越大骂倘,效果越明顯,應(yīng)用場景也更加有意義巴席。
如果 JSON 內(nèi)容太少, CJSON的資料可能反而會比較多历涝。
壓縮效果
下圖來自:https://www.oschina.net/p/jsonhpack
五、參考資料
作者:歐陽鵬 歡迎轉(zhuǎn)載,與人分享是進步的源泉毡证!
轉(zhuǎn)載請保留原文地址:https://blog.csdn.net/qq446282412/article/details/83856439
? 本人QQ: 3024665621
? QQ交流群: 123133153
? github.com/ouyangpeng
? oypcz@foxmail.com
如果本文對您有所幫助电爹,歡迎您掃碼下圖所示的支付寶和微信支付二維碼對本文進行打賞。