最近项目中java、Untiy两端通信用的json,json相对纯字节通信,数据包要大很多,这就涉及到数据压缩了。untiy客户端最早用的C#自带的压缩算法,但在untiy上用就抛出异常。而后我建议他们使用lzma开源压缩算法,原来用过,在此贴出来,记录一下。
1package com.kaka.util; 2 3import SevenZip.Compression.LZMA.Decoder; 4import SevenZip.Compression.LZMA.Encoder; 5import java.io.ByteArrayInputStream; 6import java.io.ByteArrayOutputStream; 7import java.io.IOException; 8import java.io.InputStream; 9import java.io.OutputStream; 10import java.io.UnsupportedEncodingException; 11import java.util.logging.Level; 12import java.util.logging.Logger; 13 14/** 15 * 7zip的lzma压缩,压缩比高,效率低 16 * 17 * @author zhoukai 18 */ 19public final class Lzma { 20 21 /** 22 * lzma压缩 23 * 24 * @param is 压缩输入流 25 * @param os 压缩后的输出流 26 */ 27 public final static void compress(InputStream is, OutputStream os) { 28 Encoder ec = new Encoder(); 29 try { 30 ec.WriteCoderProperties(os); 31 os.write(BitConverter.getBytes((long) is.available())); 32 ec.Code(is, os, -1, -1, null); 33 } catch (IOException ex) { 34 Logger.getLogger(Lzma.class.getName()).log(Level.SEVERE, null, ex); 35 } 36 } 37 38 /** 39 * lzma压缩 40 * 41 * @param is 压缩输入流 42 * @return 压缩后的输出流 43 */ 44 public final static byte[] compress(InputStream is) { 45 ByteArrayOutputStream os = new ByteArrayOutputStream(); 46 compress(is, os); 47 return os.toByteArray(); 48 } 49 50 /** 51 * lzma压缩 52 * 53 * @param src 压缩输入字节数组 54 * @return 压缩后的输出的字节数组 55 */ 56 public final static byte[] compress(byte[] src) { 57 return Lzma.compress(new ByteArrayInputStream(src)); 58 } 59 60 /** 61 * lzma压缩 62 * 63 * @param src 压缩输入字符串 64 * @return 压缩后的输出的字节数组 65 */ 66 public final static byte[] compress(String src) { 67 return Lzma.compress(src.getBytes(Charset.utf8)); 68 } 69 70 /** 71 * lzma压缩 72 * 73 * @param src 压缩输入字符串 74 * @param charset 字符串转字节的编码 75 * @return 压缩后的输出的字节数组 76 */ 77 public final static byte[] compress(String src, String charset) { 78 byte[] bytes = null; 79 try { 80 bytes = src.getBytes(charset); 81 } catch (UnsupportedEncodingException ex) { 82 Logger.getLogger(Lzma.class.getName()).log(Level.SEVERE, null, ex); 83 } 84 return Lzma.compress(bytes); 85 } 86 87 /** 88 * lzma压缩 89 * 90 * @param src 压缩输入字符串 91 * @param charset 字符串转字节的编码 92 * @return 压缩后的输出的字节数组 93 */ 94 public final static byte[] compress(String src, java.nio.charset.Charset charset) { 95 byte[] bytes = src.getBytes(charset); 96 return Lzma.compress(bytes); 97 } 98 99 /** 100 * lzma解压缩 101 * 102 * @param is 解压缩输入流 103 * @param os 解压缩后的输出流 104 */ 105 public final static void uncompress(InputStream is, OutputStream os) { 106 Decoder dc = new Decoder(); 107 int propertiesSize = 5; 108 byte[] properties = new byte[propertiesSize]; 109 try { 110 if (is.read(properties, 0, propertiesSize) != propertiesSize) { 111 throw new Exception("input .lzma file is too short"); 112 } 113 if (!dc.SetDecoderProperties(properties)) { 114 throw new Exception("Incorrect stream properties"); 115 } 116 byte[] outSizeBytes = new byte[8]; 117 is.read(outSizeBytes, 0, outSizeBytes.length); 118 long outSize = BitConverter.toLong(outSizeBytes, 0); 119 dc.Code(is, os, outSize); 120 } catch (Exception ex) { 121 Logger.getLogger(Lzma.class.getName()).log(Level.SEVERE, null, ex); 122 } 123 } 124 125 /** 126 * lzma解压缩 127 * 128 * @param is 解压缩输入流 129 * @return 解压缩后的输出字节数组 130 */ 131 public final static byte[] uncompress(InputStream is) { 132 ByteArrayOutputStream os = new ByteArrayOutputStream(); 133 try { 134 uncompress(is, os); 135 } catch (Exception ex) { 136 Logger.getLogger(Lzma.class.getName()).log(Level.SEVERE, null, ex); 137 } 138 return os.toByteArray(); 139 } 140 141 /** 142 * lzma解压缩 143 * 144 * @param src 解压缩输入字节数组 145 * @return 解压缩后的输出字节数组 146 */ 147 public final static byte[] uncompress(byte[] src) { 148 return Lzma.uncompress(new ByteArrayInputStream(src)); 149 } 150}
C#客户端代码
1using System; 2using System.IO; 3using SevenZip.Compression.LZMA; 4 5public class Lzma { 6 7 private static void Compress(Stream input, Stream output) 8 { 9 input.Position = 0; 10 Encoder ec = new Encoder(); 11 ec.WriteCoderProperties(output); 12 byte[] lenBytes = BitConverter.GetBytes((long)input.Length); 13 if(BitConverter.IsLittleEndian) 14 { 15 Array.Reverse(lenBytes); 16 } 17 output.Write(lenBytes, 0, lenBytes.Length); 18 ec.Code(input, output, -1, -1, null); 19 } 20 21 public static byte[] Compress(byte[] src) 22 { 23 Stream input = new MemoryStream(src); 24 Stream output = new MemoryStream(); 25 Compress(input, output); 26 output.Position = 0; 27 byte[] bytes = new byte[output.Length]; 28 output.Read(bytes, 0, bytes.Length); 29 input.Close(); 30 output.Flush(); 31 output.Close(); 32 return bytes; 33 } 34 35 public static byte[] CompressToUTF8Bytes(string src) 36 { 37 return Compress(System.Text.Encoding.UTF8.GetBytes(src)); 38 } 39 40 private static void Uncompress(Stream input, Stream output) 41 { 42 input.Position = 0; 43 Decoder dc = new Decoder(); 44 int propertiesSize = 5; 45 byte[] properties = new byte[propertiesSize]; 46 if (input.Read(properties, 0, propertiesSize) != propertiesSize) 47 { 48 throw new Exception("input .lzma file is too short"); 49 } 50 dc.SetDecoderProperties(properties); 51 byte[] outSizeBytes = new byte[8]; 52 input.Read(outSizeBytes, 0, outSizeBytes.Length); 53 if (BitConverter.IsLittleEndian) 54 { 55 Array.Reverse(outSizeBytes); 56 } 57 long outSize = BitConverter.ToInt64(outSizeBytes, 0); 58 dc.Code(input, output, -1, outSize, null); 59 } 60 61 public static byte[] Uncompress(byte[] src) 62 { 63 Stream input = new MemoryStream(src); 64 Stream output = new MemoryStream(); 65 Uncompress(input, output); 66 output.Position = 0; 67 byte[] bytes = new byte[output.Length]; 68 output.Read(bytes, 0, bytes.Length); 69 input.Close(); 70 output.Flush(); 71 output.Close(); 72 return bytes; 73 } 74 75 public static string UncompressToUTF8String(byte[] src) 76 { 77 return System.Text.Encoding.UTF8.GetString(Uncompress(src)); 78 } 79 80}
测试的时候,使用Lzma进行数据压缩压测效率不是很好,后面客户端人员发现了ICSharpCode.SharpZipLib这个库,于是又测试了一下gzip算法,还好java有点积累,工具类有现成的
1package com.kaka.util; 2 3import java.io.ByteArrayInputStream; 4import java.io.ByteArrayOutputStream; 5import java.io.IOException; 6import java.io.InputStream; 7import java.io.OutputStream; 8import java.io.UnsupportedEncodingException; 9import java.util.logging.Level; 10import java.util.logging.Logger; 11import java.util.zip.GZIPInputStream; 12import java.util.zip.GZIPOutputStream; 13 14/** 15 * GZIP压缩,压缩比相对lzma较低,但压缩效率高 16 * 17 * @author zhoukai 18 */ 19public final class GZip { 20 21 /** 22 * 压缩数据 23 * 24 * @param is 数据源 25 * @param os 压缩后的数据 26 */ 27 public final static void compress(InputStream is, OutputStream os) { 28 try (GZIPOutputStream gos = new GZIPOutputStream(os)) { 29 int count; 30 byte data[] = new byte[256]; 31 while ((count = is.read(data, 0, 256)) != -1) { 32 gos.write(data, 0, count); 33 } 34 gos.finish(); 35 gos.flush(); 36 } catch (Exception ex) { 37 Logger.getLogger(GZip.class.getName()).log(Level.SEVERE, null, ex); 38 } 39 } 40 41 /** 42 * 压缩 43 * 44 * @param src 压缩源 45 * @return 压缩后的输出的字节数组 46 */ 47 public final static byte[] compress(byte[] src) { 48 if (src == null) { 49 return null; 50 } 51 if (src.length == 0) { 52 return src; 53 } 54 byte[] bytes = null; 55 try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { 56 try (GZIPOutputStream zip = new GZIPOutputStream(out)) { 57 zip.write(src); 58 zip.finish(); 59 zip.flush(); 60 } 61 bytes = out.toByteArray(); 62 } catch (IOException ex) { 63 Logger.getLogger(GZip.class.getName()).log(Level.SEVERE, null, ex); 64 } 65 return bytes; 66 } 67 68 /** 69 * gzip压缩 70 * 71 * @param src 压缩输入字符串 72 * @param charset 字符串转字节的编码 73 * @return 压缩后的输出的字节数组 74 */ 75 public final static byte[] compress(String src, String charset) { 76 byte[] bytes = null; 77 try { 78 bytes = src.getBytes(charset); 79 } catch (UnsupportedEncodingException ex) { 80 Logger.getLogger(Lzma.class.getName()).log(Level.SEVERE, null, ex); 81 } 82 return compress(bytes); 83 } 84 85 /** 86 * gzip压缩 87 * 88 * @param src 压缩输入字符串 89 * @param charset 字符串转字节的编码 90 * @return 压缩后的输出的字节数组 91 */ 92 public final static byte[] compress(String src, java.nio.charset.Charset charset) { 93 byte[] bytes = src.getBytes(charset); 94 return compress(bytes); 95 } 96 97 /** 98 * 解压缩 99 * 100 * @param src 101 * @return 102 */ 103 public final static byte[] uncompress(byte[] src) { 104 if (src == null) { 105 return null; 106 } 107 if (src.length == 0) { 108 return src; 109 } 110 try (ByteArrayInputStream in = new ByteArrayInputStream(src)) { 111 try (GZIPInputStream zip = new GZIPInputStream(in)) { 112 byte[] bytes; 113 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { 114 int count; 115 bytes = new byte[256]; 116 while ((count = zip.read(bytes, 0, bytes.length)) != -1) { 117 os.write(bytes, 0, count); 118 } 119 bytes = os.toByteArray(); 120 } 121 return bytes; 122 } 123 } catch (IOException ex) { 124 Logger.getLogger(GZip.class.getName()).log(Level.SEVERE, null, ex); 125 } 126 return null; 127 } 128}
C# gzip工具类
1using System.IO; 2using ICSharpCode.SharpZipLib.GZip; 3 4 5public class GZip { 6 7 public static byte[] Compress(byte[] src) 8 { 9 MemoryStream ms = new MemoryStream(); 10 GZipOutputStream gzip = new GZipOutputStream(ms); 11 gzip.Write(src, 0, src.Length); 12 gzip.Close(); 13 byte[] bytes = ms.ToArray(); 14 ms.Close(); 15 return bytes; 16 } 17 18 public static byte[] Uncompress(byte[] src) 19 { 20 MemoryStream ms = new MemoryStream(src); 21 GZipInputStream gzip = new GZipInputStream(ms); 22 MemoryStream re = new MemoryStream(); 23 int count = 0; 24 byte[] data = new byte[2048]; 25 while ((count = gzip.Read(data, 0, data.Length)) != 0) 26 { 27 re.Write(data, 0, count); 28 } 29 byte[] depress = re.ToArray(); 30 ms.Close(); 31 re.Close(); 32 gzip.Close(); 33 return depress; 34 } 35 36 public static byte[] CompressToUTF8Bytes(string src) 37 { 38 return Compress(System.Text.Encoding.UTF8.GetBytes(src)); 39 } 40 41 public static string UncompressToUTF8String(byte[] src) 42 { 43 return System.Text.Encoding.UTF8.GetString(Uncompress(src)); 44 } 45 46}