7zip(下面简称7z)是由Igor Pavlov所开发的一种压缩格式,主要使用的压缩算法是LZMA/LZMA2。7z是一种压缩比非常高的格式,这与其压缩算法LZMA有直接关系,所以很多大文件都是用7z进行压缩的,比如游戏之类;高压缩比并非只有好处,就是7z的压缩速度非常慢(解压速度尚可)-当然,所有压缩算法都类似:高压缩比往往是解压速度慢,这实际上可以理解为CPU与内存/硬盘之间的trade off,后面我会详细聊一下压缩背后的原理与算法。
Commons Compress是少数支持7z压缩/解压的JAVA库(据我所知除了Commons Compress就只有XZ Utils了),其API也相对友好。
与zip格式不同,commons compress在解压7z时只提供了SevenZFile类,并未提供SevenZInputStream进行逐个解压的接口,这与7z文件的格式、算法都有关系,这里不再展开说明了。
好了,废话不多说,我们进入主题:
查看7z中的所有文件
我们可以通过sevenZFile.getEntries()查看7z中的所有文件,包括文件名等属性都可以在SevenZArchiveEntry中查看。相关代码如下:
SevenZFile sevenZFile = new SevenZFile(new File("/root/test.7zip"));
final Iterable entries = sevenZFile.getEntries();
for (SevenZArchiveEntry entry : entries) {
System.out.println(entry.getName());
System.out.println(entry.getCompressedSize()); // 文件压缩后大小 System.out.println(entry.getSize()); // 文件大小}
解压全部文件
解压全部文件时可以通过sevenZFile.getNextEntry遍历所有文件并进行解压,比如我要将位于/root/test.7zip位置的文件,全部解压到/tmp/output目录下,代码如下:
try (SevenZFile sevenZFile = new SevenZFile(new File("/root/test.7zip"))) {
byte[] buffer = new byte[4096];
SevenZArchiveEntry entry;
while ((entry = sevenZFile.getNextEntry()) != null) {
if (entry.isDirectory()) {
continue;
}
File outputFile = new File("/tmp/output/" + entry.getName());
if (!outputFile.getParentFile().exists()) {
outputFile.getParentFile().mkdirs();
}
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
while (sevenZFile.read(buffer) > 0) {
fos.write(buffer);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
解压特定文件
可以通过sevenZFile.getEntries(),获得所有文件后,通过遍历找到需要解压的zip文件,调用SevenZFile.getInputStream获取其InputStream,进行解压,比如需要将/root/test.7zip压缩包中,文件名为targetFile的文件,解压到/tmp/output/targetFile文件中,代码如下:
SevenZFile sevenZFile = new SevenZFile(new File("/root/test.7zip"));
final Iterable entries = sevenZFile.getEntries();
InputStream inputStream = null;
for (SevenZArchiveEntry entry : entries) {
if (entry.getName().equals("targetFile")) {
inputStream = sevenZFile.getInputStream(entry);
}
}
// 读取input stream即可完成解压byte[] buffer = new byte[4096];
File outputFile = new File("/tmp/output/targetFile");
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
while (inputStream.read(buffer) > 0) {
fos.write(buffer);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
注意:7z解压特定文件(也叫随机访问)是Compress的1.20版本以后才支持的特性
解压内存中的文件
Compress的所有压缩,都可以处理硬盘及内存中的文件,7z也不例外,这在网络IO等场景下非常实用,使用也很简单:
// 通过网络IO或其他途径,将7z文件读入内存byte[] data = XXX
SevenZFile sevenZFile = new SevenZFile(new SeekableInMemoryByteChannel(data));
注意:如果文件较大时,读入内存可能消耗很多内存资源
解压分卷7z文件
分卷文件解压也十分简单:
SevenZFile sevenZFile = new SevenZFile(MultiReadOnlySeekableByteChannel
.forFiles(new File("/root/test.7z.001"), new File("/root/test.7z.002")));
// 剩余代码类似XXX
注意:传入MultiReadOnlySeekableByteChannel.forFiles的7z分卷文件,需要按照正确的顺序排列
压缩
Compress压缩7z文件,是通过xz utils实现的,并且xz在Compress的POM中是可选依赖,因此,如果要使用Compress压缩7z文件,需要在POM中手动依赖xz utils
org.tukaani
xz
1.8
压缩功能相对解压,没有那么多选择,具体可以参见下面代码中的注释:
File dir = new File("/root/dir");
File output = new File(dir, "test.7z");
final Date accessDate = new Date();
final Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR, -1);
final Date creationDate = cal.getTime();
try (SevenZOutputFile outArchive = new SevenZOutputFile(output)) {
// 在7z中创建一个文件夹 SevenZArchiveEntry entry = outArchive.createArchiveEntry(dir, "foo/");
outArchive.putArchiveEntry(entry);
outArchive.closeArchiveEntry();
// 创建一个新的文件entry entry = new SevenZArchiveEntry();
// 文件命名 entry.setName("foo/bar");
// 文件创建时间、最后修改时间 entry.setCreationDate(creationDate);
entry.setAccessDate(accessDate);
outArchive.putArchiveEntry(entry);
// 这里是文件要写入的数据,可能是从其他文件中读取到的,也可以自己操作 byte[] data = XXX
outArchive.write(new byte[0]);
outArchive.closeArchiveEntry();
// 写入第二个文件,与第一个类似 entry = new SevenZArchiveEntry();
entry.setName("xyzzy");
outArchive.putArchiveEntry(entry);
outArchive.write(0);
outArchive.closeArchiveEntry();
// 完成7z文件写入后,需要调用finish outArchive.finish();
}
总结
以上就是Commons Compress处理7z格式的常用接口,同样的,如果使用中遇到了什么问题或bug,欢迎到Compress的JIRA上提问题,当然也是需要用英语提问的:)- ASF JIRAissues.apache.org