为什么GZip算法在Android和.net中结果不一样



为什么在Android和。net中GZip算法的结果不一样?

我在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中使用Decompress方法来解压缩。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方法。有人能帮帮我吗?

在Android版本中,您应该在关闭gz后关闭bos

同样,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使用默认的内部缓冲区大小;在调用bos.close()之前调用gz.close()(后者可能甚至不需要);先呼叫gz.finish(),再呼叫gz.close()

编辑2:

好吧,我应该提前意识到发生了什么。在我看来,GZIPOutputStream类是一个愚蠢的设计。它无法定义您想要的压缩,并且默认压缩设置为none。您需要子类化它并覆盖默认压缩。最简单的方法是:

GZIPOutputStream gz = new GZIPOutputStream(bos) {
    {
        def.setLevel(Deflater.BEST_COMPRESSION);
    }
};

这将重置GZIP用于提供最佳压缩的内部平减指数。(顺便说一下,如果你不熟悉它,我在这里使用的语法被称为实例初始化块。)

根据这个答案,我有4种方法。Android和。net的压缩和解压方法。这些方法彼此兼容,只有一个例外。

主要区别在于。net代码将压缩数据的长度放入二进制数据的前四个字节中。您的Java代码不会这样做。缺少长度字段

当您解压缩它时,您期望的长度在前四个字节,并在位置4开始GZIP解压缩(跳过前四个字节)。

最新更新