使用Java提取.rar文件

19
我正在寻找使用Java解压.rar文件的方法,但无论我搜索哪里,都只能得到同一个工具 - JavaUnRar。我一直在研究如何使用它来解压缩.rar文件,但是我似乎找到的所有方法都很冗长且不方便,比如这个例子
目前我能够用不到20行代码提取.tar.tar.gz.zip.jar文件,因此必须有更简单的方法来提取.rar文件,有人知道吗?
如果对任何人有帮助,这就是我用来提取.zip.jar文件的代码,对两者都有效。
 public void getZipFiles(String zipFile, String destFolder) throws IOException {
    BufferedOutputStream dest = null;
    ZipInputStream zis = new ZipInputStream(
                                       new BufferedInputStream(
                                             new FileInputStream(zipFile)));
    ZipEntry entry;
    while (( entry = zis.getNextEntry() ) != null) {
        System.out.println( "Extracting: " + entry.getName() );
        int count;
        byte data[] = new byte[BUFFER];

        if (entry.isDirectory()) {
            new File( destFolder + "/" + entry.getName() ).mkdirs();
            continue;
        } else {
            int di = entry.getName().lastIndexOf( '/' );
            if (di != -1) {
                new File( destFolder + "/" + entry.getName()
                                             .substring( 0, di ) ).mkdirs();
            }
        }
        FileOutputStream fos = new FileOutputStream( destFolder + "/"
                                                     + entry.getName() );
        dest = new BufferedOutputStream( fos );
        while (( count = zis.read( data ) ) != -1) 
            dest.write( data, 0, count );
        dest.flush();
        dest.close();
    }
}

从您压缩文件的种类来看,我猜您是在Linux环境下。为什么不从Java中调用shell命令呢?这样会更简洁快速。 - Subir Kumar Sao
不,我实际上是在使用Windows,但我目前正在开发的应用程序需要能够解压.tar.gz文件,所以我必须这样做...我希望它是一个独立的应用程序,因此如果可能的话,我不想在应用程序外部进行调用。 - flexinIT
5个回答

21

由于Java SDK中内置了多个压缩算法,因此您可以提取.gz.zip.jar文件。

RAR格式的情况略有不同。RAR是一种专有的存档文件格式。RAR许可证不允许将其包含在像Java SDK这样的软件开发工具中。

解压缩您的文件的最佳方法是使用第三方库,例如junrar

您可以在SO问题RAR archives with java中找到其他Java RAR库的参考资料。同时SO问题How to compress text file to rar format using java program也详细说明了不同的解决方法(例如使用Runtime)。


1
我刚刚在Mac上安装了junrar,它运行得非常好。它需要Apache Commons、Apache Commons VFS和log4j。 - dev4life
但如果它是一种专有格式,VFS如何使用它?我在它的代码中没有找到Runtime方法。 - Acuna

4
你可以使用http://sevenzipjbind.sourceforge.net/index.html。此版本(16.02-2.01)除了支持大量的档案格式外,还完全支持RAR5提取,包括:

  • 有密码保护的档案
  • 具有加密头的档案
  • 分卷压缩的档案

Gradle

implementation 'net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01'
implementation 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01'

或者Maven。
<dependency>
    <groupId>net.sf.sevenzipjbinding</groupId>
    <artifactId>sevenzipjbinding</artifactId>
    <version>16.02-2.01</version>
</dependency>
<dependency>
    <groupId>net.sf.sevenzipjbinding</groupId>
    <artifactId>sevenzipjbinding-all-platforms</artifactId>
    <version>16.02-2.01</version>
</dependency>

And code example


import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;

import java.io.*;
import java.util.HashMap;
import java.util.Map;

/**
 * Responsible for unpacking archives with the RAR extension.
 * Support Rar4, Rar4 with password, Rar5, Rar5 with password.
 * Determines the type of archive itself.
 */
public class RarExtractor {

    /**
     * Extracts files from archive. Archive can be encrypted with password
     *
     * @param filePath path to .rar file
     * @param password string password for archive
     * @return map of extracted file with file name
     * @throws IOException
     */
    public Map<InputStream, String> extract(String filePath, String password) throws IOException {
        Map<InputStream, String> extractedMap = new HashMap<>();

        RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
        RandomAccessFileInStream randomAccessFileStream = new RandomAccessFileInStream(randomAccessFile);
        IInArchive inArchive = SevenZip.openInArchive(null, randomAccessFileStream);

        for (ISimpleInArchiveItem item : inArchive.getSimpleInterface().getArchiveItems()) {
            if (!item.isFolder()) {
                ExtractOperationResult result = item.extractSlow(data -> {
                    extractedMap.put(new BufferedInputStream(new ByteArrayInputStream(data)), item.getPath());

                    return data.length;
                }, password);

                if (result != ExtractOperationResult.OK) {
                    throw new RuntimeException(
                            String.format("Error extracting archive. Extracting error: %s", result));
                }
            }
        }

        return extractedMap;
    }
}

附言:@BorisBrodski https://github.com/borisbrodski 祝你40岁生日快乐!希望你度过了愉快的庆祝活动。感谢你的工作!


2
你可以使用库junrar
<dependency>
   <groupId>com.github.junrar</groupId>
   <artifactId>junrar</artifactId>
   <version>0.7</version>
</dependency>

代码示例:
File f = new File(filename);
Archive archive = new Archive(f);
archive.getMainHeader().print();
FileHeader fh = archive.nextFileHeader();
while(fh!=null){        
    File fileEntry = new File(fh.getFileNameString().trim());
    System.out.println(fileEntry.getAbsolutePath());
    FileOutputStream os = new FileOutputStream(fileEntry);
    archive.extractFile(fh, os);
    os.close();
    fh=archive.nextFileHeader();
}

1
注意,junrar不支持RAR v5。 - bartektartanus

1
你可以在代码中添加以下的Maven依赖:
<dependency>
    <groupId>com.github.junrar</groupId>
    <artifactId>junrar</artifactId>
    <version>0.7</version>
</dependency>

然后使用以下代码来提取rar文件:

        File rar = new File("path_to_rar_file.rar");
    File tmpDir = File.createTempFile("bip.",".unrar");
    if(!(tmpDir.delete())){
        throw new IOException("Could not delete temp file: " + tmpDir.getAbsolutePath());
    }
    if(!(tmpDir.mkdir())){
        throw new IOException("Could not create temp directory: " + tmpDir.getAbsolutePath());
    }
    System.out.println("tmpDir="+tmpDir.getAbsolutePath());
    ExtractArchive extractArchive = new ExtractArchive();
    extractArchive.extractArchive(rar, tmpDir);
    System.out.println("finished.");

0

如果您有输入流,此方法可帮助从rar(RAR5)文件流中提取文件到流中。在我的情况下,我正在处理来自电子邮件的MimeBodyPart。

@Alexey Bril的示例对我无效。

依赖项相同

Gradle

implementation 'net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01'
implementation 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01'

马文

<dependency>
    <groupId>net.sf.sevenzipjbinding</groupId>
    <artifactId>sevenzipjbinding</artifactId>
    <version>16.02-2.01</version>
</dependency>
<dependency>
    <groupId>net.sf.sevenzipjbinding</groupId>
    <artifactId>sevenzipjbinding-all-platforms</artifactId>
    <version>16.02-2.01</version>
</dependency>  

代码

private List<InputStream> getInputStreamsFromRar5InputStream(InputStream is) throws IOException {

    List<InputStream> inputStreams = new ArrayList<>();
    
    File tempFile = File.createTempFile("tempRarArchive-", ".rar", null);

    try (FileOutputStream fos = new FileOutputStream(tempFile)) {
        fos.write(is.readAllBytes());
        fos.flush();
        try (RandomAccessFile raf = new RandomAccessFile(tempFile, "r")) {// open for reading
            try (IInArchive inArchive = SevenZip.openInArchive(null, // autodetect archive type
                    new RandomAccessFileInStream(raf))) {
                // Getting simple interface of the archive inArchive
                ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();

                for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                    if (!item.isFolder()) {
                        ExtractOperationResult result;
                        final InputStream[] IS = new InputStream[1];

                        final Integer[] sizeArray = new Integer[1];
                        result = item.extractSlow(new ISequentialOutStream() {
                            /**
                             * @param bytes of extracted data
                             * @return size of extracted data
                             */
                            @Override
                            public int write(byte[] bytes) {
                                InputStream is = new ByteArrayInputStream(bytes);
                                sizeArray[0] = bytes.length;
                                IS[0] = new BufferedInputStream(is); // Data to write to file
                                return sizeArray[0];
                            }
                        });

                        if (result == ExtractOperationResult.OK) {
                            inputStreams.add(IS[0]);
                        } else {
                            log.error("Error extracting item: " + result);
                        }
                    }
                }
            }
        }
        
    } finally {
        tempFile.delete();
    }

    return inputStreams;

}

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