3

使用以下 Java 代码将 bytes[] 压缩/解压缩到 GZIP。第一个文本字节到 gzip 字节:

public static byte[] fromByteToGByte(byte[] bytes) {
    ByteArrayOutputStream baos = null;
    try {
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        baos = new ByteArrayOutputStream();
        GZIPOutputStream gzos = new GZIPOutputStream(baos);
        byte[] buffer = new byte[1024];
        int len;
        while((len = bais.read(buffer)) >= 0) {
            gzos.write(buffer, 0, len);
        }
        gzos.close();
        baos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return(baos.toByteArray());
}

然后以另一种方式将字节压缩为未压缩字节的方法:

public static byte[] fromGByteToByte(byte[] gbytes) {
    ByteArrayOutputStream baos = null;
    ByteArrayInputStream bais = new ByteArrayInputStream(gbytes);
    try {
        baos = new ByteArrayOutputStream();
        GZIPInputStream gzis = new GZIPInputStream(bais);
        byte[] bytes = new byte[1024];
        int len;
        while((len = gzis.read(bytes)) > 0) {
            baos.write(bytes, 0, len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return(baos.toByteArray());
}

认为有什么影响,因为我没有写出一个 gzip 文件?
我还注意到,在标准 C# 函数中,BitConverter 读取前四个字节,然后调用 MemoryStream Write 函数,起点为 4,输入缓冲区长度为 4。那么这会影响标头的有效性吗?

吉姆

4

2 回答 2

8

我试过了,我无法重现您的“无效 GZip 标头”问题。这是我所做的:

Java端

我将您的 Java 压缩方法与这个 java 片段一起使用:

public static String ToHexString(byte[] bytes){
    StringBuilder hexString = new StringBuilder();
        for (int i = 0; i < bytes.length; i++)
            hexString.append((i == 0 ? "" : "-") + 
                Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
    return hexString.toString();
}

所以这个简约的java应用程序,获取测试字符串的字节,压缩它,并将其转换为压缩数据的十六进制字符串......:

public static void main(String[] args){
    System.out.println(ToHexString(fromByteToGByte("asdf".getBytes())));
}

...输出以下内容(我添加了注释)

1f-8b-08-00-00-00-00-00-00-00-4b-2c-4e-49-03-00-bd-f3-29-51-04-00-00-00
^------- GZip Header -------^ ^----------- Compressed data -----------^

C#端

我写了两种方法来将一个字节数组压缩和解压缩到另一个字节数组(压缩方法只是为了完整性,我的测试)

public static byte[] Compress(byte[] uncompressed)
{
    using (MemoryStream ms = new MemoryStream())
    using (GZipStream gzs = new GZipStream(ms, CompressionMode.Compress))
    {
        gzs.Write(uncompressed, 0, uncompressed.Length);
        gzs.Close();
        return ms.ToArray();
    }
}

public static byte[] Decompress(byte[] compressed)
{
    byte[] buffer = new byte[4096];
    using (MemoryStream ms = new MemoryStream(compressed))
    using (GZipStream gzs = new GZipStream(ms, CompressionMode.Decompress))
    using (MemoryStream uncompressed = new MemoryStream())
    {
        for (int r = -1; r != 0; r = gzs.Read(buffer, 0, buffer.Length))
            if (r > 0) uncompressed.Write(buffer, 0, r);
        return uncompressed.ToArray();
    }
}

连同一个接受十六进制字符串并将其转换回字节数组的小函数...... (也仅用于测试目的)

public static byte[] ToByteArray(string hexString)
{
    hexString = hexString.Replace("-", "");
    int NumberChars = hexString.Length;
    byte[] bytes = new byte[NumberChars / 2];
    for (int i = 0; i < NumberChars; i += 2)
        bytes[i / 2] = Convert.ToByte(hexString.Substring(i, 2), 16);
    return bytes;
}

...我做了以下事情:

// Just hardcoded the output of the java program, convert it back to byte[]
byte[] fromjava = ToByteArray("1f-8b-08-00-00-00-00-00-00-00-" + 
                  "4b-2c-4e-49-03-00-bd-f3-29-51-04-00-00-00");

// Decompress it with my function above
byte[] uncompr = Decompress(fromjava);

// Get the string out of the byte[] and print it
Console.WriteLine(System.Text.ASCIIEncoding.ASCII
                    .GetString(uncompr, 0, uncompr.Length));

瞧,输出是:

asdf

非常适合我。也许你应该在你的 c# 应用程序中检查你的解压方法。

您在上一个问题中说过您将这些字节数组存储在数据库中,对吗?也许您想检查字节是否以您放入它们的方式从数据库中返回。

于 2010-04-29T18:25:41.733 回答
0

将此作为答案发布,以便代码看起来不错。请注意几件事:
首先,到数据库的往返似乎没有任何影响。双方的 Java 都产生了我所输入的内容。C# 中的 Java 与 Ionic API 配合得很好,C# 中的 Java 也是如此。这让我想到了第二点。其次,我原来的解压顺序是:

public static string Decompress(byte[] gzBuffer)
{
    using (MemoryStream ms = new MemoryStream())
    {
        int msgLength = BitConverter.ToInt32(gzBuffer, 0);
        ms.Write(gzBuffer, 4, gzBuffer.Length – 4);
        byte[] buffer = new byte[msgLength];
        ms.Position = 0;
        using (GZipStream zip = new GZipStream(ms, CompressionMode.Decompress))
        {
            zip.Read(buffer, 0, buffer.Length);
        }
        return Encoding.UTF8.GetString(buffer);
    } 
}  

这取决于内部字节数,无论内部值如何,您都会读取整个文件。不知道Ionic算法是什么。您的工作方式与我使用的 Java 方法相同。这是我看到的唯一区别。非常感谢您完成所有这些工作。我会记住这样做的方式。谢谢,吉姆

于 2010-04-29T19:36:27.623 回答