以gzip格式包装缩小的数据

时间:2012-10-28 00:51:38

标签: java multithreading gzip concatenation deflate

我想我错过了很简单的事情。我有一个字节数组,使用Deflater:

将溢出的数据写入其中
deflate(outData, 0, BLOCK_SIZE, SYNC_FLUSH)

我不仅仅使用GZIPOutputStream的原因是因为有4个线程(变量),每个线程都被赋予一个数据块,每个线程在将压缩数据存储到全局字节数组之前压缩它自己的块。如果我使用GZIPOutputStream它会混淆格式,因为每个小块都有一个标题和预告片,它是自己的gzip数据(我只想压缩它)。

所以最后,我得到了这个byteArray,outData,它保存了我所有的压缩数据,但我不确定如何包装它。 GZIPOutputStream从具有未压缩数据的缓冲区写入,但此数组全部已设置。它已经被压缩了,我只是试图弄清楚如何将它变成一个表格。

编辑:好的,我的措辞不好。我将它写入输出,而不是文件,以便在需要时可以重定向。一个非常简单的例子是

cat file.txt | java Jzip | gzip -d | cmp file.txt

应该返回0.现在的问题是,如果我将这个字节数组按原样写入输出,它只是“原始”压缩数据。我认为gzip需要所有这些额外的信息。

如果有替代方法,那就没问题了。这样的全部原因是因为我需要使用多个线程。否则我只会调用GZIPOutputStream。

DOUBLE EDIT:由于评论提供了很多好的见解,另一种方法是我只有一堆未压缩的数据块,这些数据块最初是一个长流。如果gzip可以读取连接流,如果我接受了这些块(并按顺序保存它们)并将每个块分配给一个在自己的块上调用GZIPOutputStream的线程,然后取结果并连接它们。实质上,每个块现在都有标题,压缩信息和预告片。如果我连接它们,gzip会认识到吗?

示例:

cat file.txt
Hello world! How are you? I'm ready to set fire to this assignment.

java Testcase < file.txt > file.txt.gz

所以我从输入中接受它。在程序内部,流被分成 “你好,世界!” “你好吗?” “我准备放火烧这个任务”(它们不是字符串,它只是一个字节数组!这只是插图)

所以我有三个字节块,都是未压缩的。我将每个块都给了一个使用

的线程
public static class DGZIPOutputStream extends GZIPOutputStream
{
    public DGZIPOutputStream(OutputStream out, boolean flush) throws IOException
    {
        super(out, flush);
    }
    public void setDictionary(byte[] b)
    {
        def.setDictionary(b);
    }
    public void updateCRC(byte[] input)
    {
        crc.update(input);
    }                       
}

正如你所看到的,这里唯一的事情就是我将flush设置为SYNC_FLUSH,这样我就可以正确对齐并能够设置字典。如果每个线程都使用DGZIPOutputStream(我已经测试了它并且它适用于一个长连续输入),并且我连接了这三个块(现在用头和尾部压缩),gzip -d file.txt.gz工作?

如果这太奇怪了,请完全忽略字典。这并不重要。我只是把它添加进去了。

3 个答案:

答案 0 :(得分:6)

如果在使用nowrap(原文如此)构造函数时将Deflater设置为true,则结果为raw deflate。否则它是zlib,你必须剥离zlib头和预告片。对于答案的其余部分,我假设nowrap是真的。

要将完整的,已终止的deflate流包装为gzip流,您需要预先添加十个字节:

"\x1f\x8b\x08\0\0\0\0\0\0\xff"

(抱歉 - C格式,你需要转换为Java八进制)。您还需要以小端顺序附加四字节CRC,然后以小端顺序附加四字节总未压缩长度模2 ^ 32。鉴于标准Java API中可用的内容,您需要以串行方式计算CRC。它不能并行完成。 zlib确实有一个函数来组合并行计算的单独的CRC,但是没有用Java公开。

请注意,我说了一个完整的,终止的deflate流。制作具有并行放气任务的其中一个需要一些小心。您需要使n-1未终止的deflate流和一个最终终止的deflate流并连接这些流。最后一个是正常的。需要使用同步刷新终止另一个n-1,以便在字节边界上结束每个{1}}并且不将其标记为流的结尾。为此,您使用deflate和flush参数SYNC_FLUSH。不要在finish()上使用{{1}}。

为了更好地进行压缩,您可以在每个块上使用setDictionary和前一个块的最后一个32K。

答案 1 :(得分:0)

如果您想在文件中写outdata,可以写成:

GZIPOutputStream outStream= new GZIPOutputStream(new FileOutputStream("fileName"));
outStream.write(outData, 0, outData.length);
outStream.close();

或者只需使用java.io.FileOutputStream来写:

FileOutputStream outStream= new FileOutputStream("fileName");
outStream.write(outData, 0, outData.length);
outStream.close();

答案 2 :(得分:0)

您只想将字节数组 - 按原样 - 写入文件?

您可以使用Apache Commons:

FileOutputStream fos = new FileOutputStream("yourFilename");
fos.write(outData);
fos.close():

或普通的旧Java:

BufferedOutputStream bs = null;

try {
    FileOutputStream fs = new FileOutputStream(new File("yourFilename"));
    bs = new BufferedOutputStream(fs);
    bs.write(outData);
    bs.close();

} catch (Exception e) {
    //please handle this
}

if (bs != null) try { 
    bs.close(); 
} catch (Exception e) {
    //please handle this
}