如何使用Apache Commons解压缩TAR文件

7

我正在使用Apache Commons 1.4.1库来压缩和解压缩".tar.gz"文件。

我在最后一步遇到了问题 -- 将TarArchiveInputStream转换为FileOutputStream。

奇怪的是,它在这一行出错:

FileOutputStream fout = new FileOutputStream(destPath);

destPath是一个文件,其规范路径为:C:\Documents and Settings\Administrator\My Documents\JavaWorkspace\BackupUtility\untarred\Test\subdir\testinsub.txt

产生的错误:

Exception in thread "main" java.io.IOException: The system cannot find the path specified

有任何想法是什么?为什么它找不到路径?
我在下面附上整个方法(其中大部分内容摘自这里)。
private void untar(File dest) throws IOException {
    dest.mkdir();
    TarArchiveEntry tarEntry = tarIn.getNextTarEntry();
    // tarIn is a TarArchiveInputStream
    while (tarEntry != null) {// create a file with the same name as the tarEntry
        File destPath = new File(dest.toString() + System.getProperty("file.separator") + tarEntry.getName());
        System.out.println("working: " + destPath.getCanonicalPath());
        if (tarEntry.isDirectory()) {
            destPath.mkdirs();
        } else {
            destPath.createNewFile();
            FileOutputStream fout = new FileOutputStream(destPath);
            tarIn.read(new byte[(int) tarEntry.getSize()]);
            fout.close();
        }
        tarEntry = tarIn.getNextTarEntry();
    }
    tarIn.close();
}

有些不好意思问,但我尝试使用您的代码示例,并发现它可以在我正在处理的特定gzip文件上工作。既然从输入流中读取了内容,为什么没有调用fout.write(...)就能正常工作呢?在用户@user1894600的回答中,他必须显式调用write(...)并提供已读入内存的字节数组。 - blong
2个回答

18

您的程序出现了Java堆空间错误。 因此我认为需要做一些小改动。 以下是代码...

public static void uncompressTarGZ(File tarFile, File dest) throws IOException {
    dest.mkdir();
    TarArchiveInputStream tarIn = null;

    tarIn = new TarArchiveInputStream(
                new GzipCompressorInputStream(
                    new BufferedInputStream(
                        new FileInputStream(
                            tarFile
                        )
                    )
                )
            );

    TarArchiveEntry tarEntry = tarIn.getNextTarEntry();
    // tarIn is a TarArchiveInputStream
    while (tarEntry != null) {// create a file with the same name as the tarEntry
        File destPath = new File(dest, tarEntry.getName());
        System.out.println("working: " + destPath.getCanonicalPath());
        if (tarEntry.isDirectory()) {
            destPath.mkdirs();
        } else {
            destPath.createNewFile();
            //byte [] btoRead = new byte[(int)tarEntry.getSize()];
            byte [] btoRead = new byte[1024];
            //FileInputStream fin 
            //  = new FileInputStream(destPath.getCanonicalPath());
            BufferedOutputStream bout = 
                new BufferedOutputStream(new FileOutputStream(destPath));
            int len = 0;

            while((len = tarIn.read(btoRead)) != -1)
            {
                bout.write(btoRead,0,len);
            }

            bout.close();
            btoRead = null;

        }
        tarEntry = tarIn.getNextTarEntry();
    }
    tarIn.close();
} 

祝你好运


那么,当声明为 byte [] btoRead = new byte[(int)tarEntry.getSize()]; 时,堆空间错误会发生,因为字节数组可能太大了。 - blong
2
非常好的回应。然而,以下的 deskPath.createNewFile(); 应该被修改以创建父目录 if (!destPath.getParentFile().exists()) { destPath.getParentFile().mkdirs(); } destPath.createNewFile(); - Ross H Mills III

5
一些通用的要点,为什么你要用File构造函数,而不是使用一种完美可用的构造函数来定义所需创建的File名称并指定父级文件?
另外,我不太确定在Windows路径中如何处理空格。这可能是你问题的原因。尝试使用上面提到的构造函数,看看它是否有所不同:File destPath = new File(dest, tarEntry.getName());(假设File dest是一个合法的文件,并且可以被访问)。
第三, 在对File对象进行任何操作之前,应先检查其是否存在且是否可访问。这将极大帮助你准确找到问题所在。

感谢您的回复。我决定重写这个模块,现在它运行得很好。我采纳了您关于不要操作文件对象的建议,因此我将把您的答案标记为正确的(基于原则)。 - Redandwhite
很高兴能帮到你,希望最终一切都能顺利解决。祝好运 :) - posdef
我正在使用相同的代码来untar .tar文件而不是.tar.gz文件。但是从这一行代码“new File(dest,tarEntry.getName())”中,我获取到的是文件内容而不是文件名。 我应该怎么做才能在.tar中获取文件名呢? - Parita Porwal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接