1. 概述
GZIP 格式是一种用于数据压缩的文件格式。Java 语言中的 GZipInputStream
和 GZipOutputStream
类实现了这种文件格式。
在这个教程中,我们将学习如何在 Java 中使用 GZIP 压缩数据,并查看如何将压缩后的数据写入字节数组。
2. GZipOutputStream
类
GZipOutputStream
类负责压缩并写入底层输出流的数据。
2.1. 对象实例化
我们可以使用构造函数创建此类的对象:
ByteArrayOutputStream os = new ByteArrayOutputStream();
GZIPOutputStream gzipOs = new GZIPOutputStream(os);
在这里,我们向构造函数传递一个 ByteArrayOutputStream
对象。这样,之后我们可以通过调用 toByteArray()
方法获取压缩后的数据作为字节数组。
除了 ByteArrayOutputStream
,我们还可以提供其他 OutputStream
的实例,例如:
-
FileOutputStream
:将数据存储在文件中 -
ServletOutputStream
:通过网络传输数据
在两种情况下,数据都会随着到来而发送到其目的地。
2.2. 压缩数据
write()
方法执行数据压缩:
byte[] buffer = "Sample Text".getBytes();
gzipOs.write(buffer, 0, buffer.length);
write()
方法会压缩 buffer
字节数组的内容,并将其写入包装的输出流。
**除了 buffer
字节数组,write()
还包括两个额外参数 offset
和 length
**。它们定义了字节数组内部的一段范围。因此,我们可以使用这些参数指定要写入的字节范围,而不是整个 buffer
。
最后,为了完成数据压缩,我们需要调用 close()
:
gzipOs.close();
close()
方法会写入所有剩余数据并关闭流。因此,调用 close()
很重要,否则我们将丢失数据。
3. 获取压缩后的字节数组
我们将创建一个用于 GZIP 压缩数据的实用方法,同时也会看到如何获取包含压缩数据的字节数组。
3.1. 压缩数据
让我们创建一个名为 gzip()
的方法,用于以 GZIP 格式压缩数据:
private static final int BUFFER_SIZE = 512;
public static void gzip(InputStream is, OutputStream os) throws IOException {
GZIPOutputStream gzipOs = new GZIPOutputStream(os);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead = 0;
while ((bytesRead = is.read(buffer)) > -1) {
gzipOs.write(buffer, 0, bytesRead);
}
gzipOs.close();
}
在这个方法中,首先我们创建一个新的 GZIPOutputStream
实例。然后,我们开始从 is
输入流中复制数据,使用 buffer
字节数组。
值得注意的是,我们会一直读取字节,直到返回值为 -1
。**当到达流的末尾时,read()
方法会返回 -1
**。
3.2. 获取包含压缩数据的字节数组
我们将压缩一个字符串并将结果写入字节数组。我们将使用之前创建的 gzip()
方法:
String payload = "This is a sample text to test the gzip method. Have a nice day!";
ByteArrayOutputStream os = new ByteArrayOutputStream();
gzip(new ByteArrayInputStream(payload.getBytes()), os);
byte[] compressed = os.toByteArray();
在这里,我们向 gzip()
方法提供输入和输出流。我们将 payload
值封装在一个 ByteArrayInputStream
对象中。然后,我们创建一个空的 ByteArrayOutputStream
,gzip()
将在其中写入压缩数据。
最后,调用 gzip()
之后,我们可以使用 toByteArray()
方法获取压缩后的数据。
4. 测试
在测试我们的代码之前,让我们将 gzip()
方法添加到 GZip
类中。现在,我们准备好使用单元测试来测试我们的代码:
@Test
void whenCompressingUsingGZip_thenGetCompressedByteArray() throws IOException {
String payload = "This is a sample text to test method gzip. The gzip algorithm will compress this string. "
+ "The result will be smaller than this string.";
ByteArrayOutputStream os = new ByteArrayOutputStream();
GZip.gzip(new ByteArrayInputStream(payload.getBytes()), os);
byte[] compressed = os.toByteArray();
assertTrue(payload.getBytes().length > compressed.length);
assertEquals("1f", Integer.toHexString(compressed[0] & 0xFF));
assertEquals("8b", Integer.toHexString(compressed[1] & 0xFF));
}
在这个测试中,我们压缩一个字符串值。我们将字符串转换为 ByteArrayInputStream
并将其提供给 gzip()
方法。此外,输出数据会写入 ByteArrayOutputStream
。
如果满足以下两个条件,那么测试就成功了:
- 压缩后的数据大小小于未压缩的
- 压缩后的字节数组以值
1f 8b
开始。
关于第二个条件,GZIP 文件以固定的值 1f 8b
开头,以符合 GZIP 文件格式。
因此,如果我们运行单元测试,我们将验证这两个条件都为真。
5. 总结
在这篇文章中,我们学习了如何在 Java 中使用 GZIP 文件格式获取压缩后的字节数组。为此,我们创建了一个压缩实用方法。最后,我们测试了我们的代码。
如往常一样,我们示例的完整源代码可以在 GitHub 上找到。