1

我想我错过了一些非常简单的东西。我有一个字节数组,其中包含使用 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。

双重编辑:由于评论提供了很多很好的见解,另一种方法是我只有一堆未压缩的数据块,这些数据块最初是一个长流。如果 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

所以我从输入中接受它。在程序内部,流被分成“Hello world!” “你好吗?” “我准备点燃这个任务”(它们不是字符串,它只是一个字节数组!这只是说明)

所以我有这三个字节块,都是未压缩的。我将这些块中的每一个都分配给一个线程,该线程使用

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);
    }                       
}

如您所见,这里唯一的事情是我已将刷新设置为 SYNC_FLUSH,因此我可以正确对齐并能够设置字典。如果每个线程都使用 DGZIPOutputStream (我已经测试过,它适用于一个长时间的连续输入),并且我将这三个块连接起来(现在每个块都用标题和拖尾压缩),那么 gzip -d file.txt.gz 会工作吗?

如果这太奇怪了,请完全忽略字典。这并不重要。我只是在使用它的时候添加了它。

4

3 回答 3

6

如果nowrap在使用Deflater(sic) 构造函数时设置为 true,则结果为原始 deflate。否则它是 zlib,你将不得不剥离 zlib 标头和预告片。对于其余的答案,我假设nowrap是真的。

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

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

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

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

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

于 2012-10-28T06:11:06.423 回答
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();
于 2012-10-28T01:02:30.520 回答
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
}
于 2012-10-28T01:02:46.693 回答