为什么GZip算法的结果在Android和.Net中不相同?
我在android中的代码:
public static String compressString(String str) {
String str1 = null;
ByteArrayOutputStream bos = null;
try {
bos = new ByteArrayOutputStream();
BufferedOutputStream dest = null;
byte b[] = str.getBytes();
GZIPOutputStream gz = new GZIPOutputStream(bos, b.length);
gz.write(b, 0, b.length);
bos.close();
gz.close();
} catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
byte b1[] = bos.toByteArray();
return Base64.encode(b1);
}
.Net WebService中的代码:
public static string compressString(string text)
{
byte[] buffer = Encoding.UTF8.GetBytes(text);
MemoryStream ms = new MemoryStream();
using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true))
{
zip.Write(buffer, 0, buffer.Length);
}
ms.Position = 0;
MemoryStream outStream = new MemoryStream();
byte[] compressed = new byte[ms.Length];
ms.Read(compressed, 0, compressed.Length);
byte[] gzBuffer = new byte[compressed.Length + 4];
System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
return Convert.ToBase64String(gzBuffer);
}
在android中:
compressString("hello"); -> "H4sIAAAAAAAAAMtIzcnJBwCGphA2BQAAAA=="
在.Net:
compressString("hello"); -> "BQAAAB+LCAAAAAAABADtvQdgHEmWJSYvbcp7f0r1StfgdKEIgGATJNiQQBDswYjN5pLsHWlHIymrKoHKZVZlXWYWQMztnbz33nvvvffee++997o7nU4n99//P1xmZAFs9s5K2smeIYCqyB8/fnwfPyLmeVlW/w+GphA2BQAAAA=="
有趣的是,当我在android中使用解压缩方法解压缩.Net compressString 方法的结果时,它会正确返回原始字符串但是当我得到错误时我会收到错误解压缩android compressedString 方法的结果。
Android解压缩方法:
public static String Decompress(String zipText) throws IOException {
int size = 0;
byte[] gzipBuff = Base64.decode(zipText);
ByteArrayInputStream memstream = new ByteArrayInputStream(gzipBuff, 4,
gzipBuff.length - 4);
GZIPInputStream gzin = new GZIPInputStream(memstream);
final int buffSize = 8192;
byte[] tempBuffer = new byte[buffSize];
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((size = gzin.read(tempBuffer, 0, buffSize)) != -1) {
baos.write(tempBuffer, 0, size);
}
byte[] buffer = baos.toByteArray();
baos.close();
return new String(buffer, "UTF-8");
}
我认为Android compressString 方法存在错误。 有人能帮助我吗?
答案 0 :(得分:2)
在Android版本中,您应该在关闭bos
之后关闭gz
。
此外,compressString
中的这一行可能会给您带来问题:
byte b[] = str.getBytes();
这将使用设备上的默认编码将字符转换为字节,这几乎肯定不是UTF-8。另一方面,.NET版本使用UTF8。在Android中,请尝试以下方法:
byte b[] = str.getBytes("UTF-8");
编辑:在进一步查看您的代码时,我建议您重写它:
byte b[] = str.getBytes("UTF-8");
GZIPOutputStream gz = new GZIPOutputStream(bos);
gz.write(b, 0, b.length);
gz.finish();
gz.close();
bos.close();
更改是:使用UTF-8编码字符;使用GZIPOutputStream的默认内部缓冲区大小;在调用gz.close()
之前调用bos.close()
(后者可能甚至不需要);并在致电gz.finish()
之前致电gz.close()
。
编辑2:
好的,我应该在发生之前意识到。在我看来,GZIPOutputStream类是一个愚蠢的设计。它无法定义所需的压缩,默认压缩设置为none。您需要对其进行子类化并覆盖默认压缩。最简单的方法是:
GZIPOutputStream gz = new GZIPOutputStream(bos) {
{
def.setLevel(Deflater.BEST_COMPRESSION);
}
};
这将重置GZIP用于提供最佳压缩的内部平减指数。 (顺便说一句,如果您不熟悉它,我在这里使用的语法称为instance initializer block。)
答案 1 :(得分:2)
根据this answer,我有4种方法。 Android和.net压缩和解压缩方法。除一种情况外,这些方法相互兼容。
答案 2 :(得分:0)
主要区别在于您的.NET代码将压缩数据的长度放入二进制数据的前四个字节。您的Java代码不会这样做。它缺少长度字段。
当你解压缩它时,你会想到前四个字节的长度并在位置4开始GZIP解压缩(跳过前四个字节)。