tar是一种很老的归档格式,最早可以追溯到UNIX时代,tar已成为POSIX标准之一。tar早期经常用于将文件拷贝到磁带上(这里的磁带可不是以前听歌用的磁带,而是早期计算机的存储截至),加上tar格式诞生时间很早,因此tar标准里面的很多设计今天看起来很奇怪。
对于tar格式,需要注意的是,它是一个“归档”(archive)格式。何谓归档?简单的说,就是把“散落”的文件都整齐的“码放”在一起。换句话说,tar格式只是单纯的把文件放在一起,并不能起到压缩的效果。常见的压缩格式如tar.gz,tar.bz2,tar.xz 等格式,其实是把文件通过tar放在一起之后,用gzip、bzip2、xz等工具再进行一次压缩得到的。
另外,tar格式的设计与zip、7zip等常见压缩格式有些不同-它没有类似“目录”的存在,这就导致很难做到zip、7zip一样只解压特定的文件(这种操作一般称作随机访问random access)而不去碰解压其他文件。Commons Compress对于tar格式也是这样设计的:它不提供类似ZipFile这样可以罗列所有文件的类和方法,只能按照文件在tar中的顺序去逐个遍历。
直接上例子:
解压全部文件
解压全部文件时可以通过TarArchiveInputStream.getNextTarEntry遍历所有文件并进行解压,比如我要将位于/root/test.tar位置的文件,全部解压到/tmp/output目录下,代码如下:
try (FileInputStream in = new FileInputStream(new File("/root/test.tar")));
TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(in)) {
byte[] buffer = new byte[4096];
TarArchiveEntry entry;
while ((entry = tarArchiveInputStream.getNextTarEntry()) != 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 (tarArchiveInputStream.read(buffer) > 0) {
fos.write(buffer);
}
}
}
}
解压特定文件
可以在遍历时,根据entry判断是否是需要解压的文件,比如需要将/root/test.tar压缩包中,文件名为targetFile的文件,解压到/tmp/output/targetFile文件中,代码如下:
try (FileInputStream in = new FileInputStream(getFile("bla.tar"));
TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(in)) {
byte[] buffer = new byte[4096];
TarArchiveEntry entry;
while ((entry = tarArchiveInputStream.getNextTarEntry()) != null) {
if (entry.isDirectory()) {
continue;
}
if (entry.getName() != "targetFile") {
continue;
}
File outputFile = new File("/tmp/output/" + entry.getName());
if (!outputFile.getParentFile().exists()) {
outputFile.getParentFile().mkdirs();
}
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
while (tarArchiveInputStream.read(buffer) > 0) {
fos.write(buffer);
}
}
}
}
解压tar.gz文件
我前面说过,tar.gz文件是先把文件用tar归档以后用gzip进行压缩,因此解压tar.gz也就需要先解压gzip然后解压tar。说起来好像很复杂,实际上代码和解压tar文件几乎完全一样,只需要将输入的文件用GZIPInputStream包装一下即可:
try (FileInputStream in = new FileInputStream(new GZIPInputStream(new File("/root/test.tar.gz"))));
TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(in)) {
byte[] buffer = new byte[4096];
TarArchiveEntry entry;
while ((entry = tarArchiveInputStream.getNextTarEntry()) != 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 (tarArchiveInputStream.read(buffer) > 0) {
fos.write(buffer);
}
}
}
}
创建tar文件
比如我要在/tmp/output/目录下,创建一个test.tar,并在里面添加2个文件file1和file2,代码如下:
final File f = new File("/tmp/output/test.tar");
final FileOutputStream fos = new FileOutputStream(f);
final TarArchiveOutputStream tos = new TarArchiveOutputStream(fos);
TarArchiveEntry entry = new TarArchiveEntry("file1");
final String x = "this is content of file1\n";
entry.setSize(x.length());
tos.putArchiveEntry(entry);
tos.write(x.getBytes());
tos.closeArchiveEntry();
entry = new TarArchiveEntry("file2");
final String y = "this is content of file2\n";
entry.setSize(y.length());
tos.putArchiveEntry(entry);
tos.write(y.getBytes());
tos.closeArchiveEntry();
tos.close();
创建tar.gz文件
太简单了,几乎与创建tar文件类似,只需要将文件输出流用gzip输出流包装一下即可,还是用上面的例子:
final File f = new File("/tmp/output/test.tar.gz");
final FileOutputStream fos = new FileOutputStream(f);
final GzipCompressorOutputStream gzipos = new GzipCompressorOutputStream(fos);
final TarArchiveOutputStream tos = new TarArchiveOutputStream(gzipos);
TarArchiveEntry entry = new TarArchiveEntry("file1");
final String x = "this is content of file1\n";
entry.setSize(x.length());
tos.putArchiveEntry(entry);
tos.write(x.getBytes());
tos.closeArchiveEntry();
entry = new TarArchiveEntry("file2");
final String y = "this is content of file2\n";
entry.setSize(y.length());
tos.putArchiveEntry(entry);
tos.write(y.getBytes());
tos.closeArchiveEntry();
tos.close();
还有问题?
commons社区是一个开放的社区,可以在commons compress社区上提问- ASF JIRAissues.apache.org