如何在Java中从7-zip流中提取文件而不将其存储在硬盘上?

18

我想从7-zip字节流中提取一些文件,但它不能存储在硬盘上,所以我不能使用RandomAccessFile类。我已经阅读了sevenzipjbinding源代码,它还使用了一些闭源的东西,比如由其他语言编写的lib7-Zip-JBinding.so。而官方包的方法SevenZip

SevenZip.Compression.LZMA.Decoder.Code(java.io.InputStream inStream,java.io.OutputStream outStream,long outSize,ICompressProgressInfo progress)

只能解压缩一个单一的文件。

那么我应该如何使用纯Java解压缩7-zip字节流?

有没有人有解决方案?

对不起我的英语不好,我在线等待您的答案。


1
请查看 Apache Commons Compress 库,我相信其中有7z的实现。 - MadProgrammer
3个回答

17

Commons compress 1.6及以上版本支持操作7z格式。尝试一下吧。

参考资料:

http://commons.apache.org/proper/commons-compress/index.html

示例:

    SevenZFile sevenZFile = new SevenZFile(new File("test-documents.7z"));
    SevenZArchiveEntry entry = sevenZFile.getNextEntry();
    while(entry!=null){
        System.out.println(entry.getName());
        FileOutputStream out = new FileOutputStream(entry.getName());
        byte[] content = new byte[(int) entry.getSize()];
        sevenZFile.read(content, 0, content.length);
        out.write(content);
        out.close();
        entry = sevenZFile.getNextEntry();
    }
    sevenZFile.close();

1
提示:7-Zip字节流是一个套接字流(或HTTP请求流),而不是存储在磁盘上的文件,我们甚至不能使用File类。我们最近将压缩分辨率更改为xz,因为7z不适用完全内存分辨率。 - user3330817
4
我在安卓上尝试了Apache Commom-Compress并想在这里分享我的经验。这个解决方案完全不符合我的目标,因为它需要3个小时(三个小时)才能将一个250 MB的文件解压至750 MB。看起来实现非常缓慢且没有优化。我的手机是Nexus 5。 - Oleksii K.
2
Commons Compress库确实可以操作7z格式,但显然不能像OP所要求的那样从流中读取。 - Unda

3
由于7z解压缩需要随机访问,您必须将整个流读入 byte[] 中,并使用new SevenZFile(new SeekableInMemoryByteChannel(bytes))。 (如果它的长度超过 Integer.MAX_VALUE 字节,则必须创建自定义 SeekableByteChannel 实现。)一旦实例构造完成,该过程与SANN3答案中的过程相同。
如果它无法适应内存并且您无法将其写入临时文件,则7zip不是适合的压缩算法,因为它需要随机访问。

0

我的代码可以正常运行:

需要添加的pom.xml文件:

<!-- Unzip 7z -->
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.20</version>
</dependency>
<!-- Unzip 7z dependancy with commons-compress for 7z-->
<!-- https://mvnrepository.com/artifact/org.tukaani/xz -->
<dependency>
    <groupId>org.tukaani</groupId>
    <artifactId>xz</artifactId>
    <version>1.8</version>
</dependency>

添加到类中

public static String unzip(final String directory, final String fileName) {
    final StringBuilder sb = new StringBuilder();
    final File fDirectory = new File(directory);
    final File input7z = new File(fDirectory, fileName);
    try (final SevenZFile sevenZFile = new SevenZFile(input7z)) {
        SevenZArchiveEntry entry = sevenZFile.getNextEntry();
        while (entry != null) {
            LOGGER.info("Treatment of entry : {}", entry.getName());
            try (final FileOutputStream out = new FileOutputStream(new File(fDirectory, entry.getName()))) {
                byte[] content = new byte[(int) entry.getSize()];
                sevenZFile.read(content, 0, content.length);
                out.write(content);
            } catch (final IOException ioe) {
                final String error = "Error when writing entry " + entry.getName();
                LOGGER.error(error);
                sb.append(error).append("\n");
            }
            entry = sevenZFile.getNextEntry();
        }
    } catch (final IOException ioe) {
        final String error = "Error when reading entry " + fileName;
        LOGGER.error(error);
        sb.append(error).append("\n");
    }
    return sb.length() == 0 ? null : sb.toString();
}

更新:

无存储:

public static String getContentOneFile(final String directory, final String fileName) throws IOException {
    final List<String> subFilenames = ls(directory, fileName);
    if (subFilenames.size() == 1) {
        return getContent(directory, fileName, subFilenames.get(0));
    } else if (subFilenames.size() == 0) {
        throw new IOException("No file found in 7zip : " + directory + ":" + fileName);
    } else {
        throw new IOException("Can not extract data from many document. Please specify subFilename in " + subFilenames);
    }
}

使用 ls 方法:

    public static List<String> ls(final String directory, final String fileName) throws IOException {
        final List<String> out = new ArrayList<>();
        final File fDirectory = new File(directory);
        final File input7z = new File(fDirectory, fileName);
        try (final SevenZFile sevenZFile = new SevenZFile(input7z)) {
            SevenZArchiveEntry entry = sevenZFile.getNextEntry();
            while (entry != null) {
                out.add(entry.getName());
                entry = sevenZFile.getNextEntry();
            }
        } catch (final IOException ioe) {
            final String error = "Error when reading entry " + fileName;
            LOGGER.error(error);
            throw ioe;
        }
        return out;
    }

而且使用getContent方法:

   public static String getContent(final String directory, final String fileName, final String subFileName) throws IOException {
        String out = null;
        final File fDirectory = new File(directory);
        final File input7z = new File(fDirectory, fileName);
        try (final SevenZFile sevenZFile = new SevenZFile(input7z)) {
            SevenZArchiveEntry entry = sevenZFile.getNextEntry();
            while (entry != null) {
                LOGGER.info("Treatment of entry : {}", entry.getName());
                if (subFileName.equals(entry.getName())) {
                    byte[] content = new byte[(int) entry.getSize()];
                    sevenZFile.read(content, 0, content.length);
                    out = new String(content);
                }
                entry = sevenZFile.getNextEntry();
            }
        } catch (final IOException ioe) {
            final String error = "Error when reading entry " + fileName;
            LOGGER.error(error);
            throw ioe;
        }
        return out;
    }

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