how to extract files from a 7-zip stream in Java without store it on hard disk?
Asked Answered
S

3

18

I want to extract some files from a 7-zip byte stream,it can't be stored on hard disk,so I can't use RandomAccessFile class,I have read sevenzipjbinding source code,it also uncompresses the file with some closed source things like lib7-Zip-JBinding.so which wrote by other language.And the method of the official package SevenZip

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

can only uncompress a single file.

So how could I uncompress a 7-zip byte stream with pure Java?

Any guys have solution?

Sorry for my poor English and I'm waiting for your answers online.

Statued answered 20/2, 2014 at 3:7 Comment(1)
Take a look at the Apache Commons, Compress library which I believe has a 7z implementationConfidence
B
17

Commons compress 1.6 and above has support for manipulating 7z format. Try it.

Reference :

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

Sample :

    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();
Beisel answered 20/2, 2014 at 5:0 Comment(3)
tips:the 7-zip byte stream is a socket stream(or http request stream),not a file stored on disk,we can't even use File class.we recently change the compress resolution to xz because 7z doesn't apply a fully-memory resolution.Statued
I tried Apache Commom-Compress on Android and I want to share my experience here. This solution is completely unacceptable for my goals, because it takes 3 hours (three hours) to decompress single file from 250 Mb to 750 Mb. It seems the implementation is very slow and not optimized. My phone is Nexus 5.Immigrant
It is true that the Commons Compress library can manipulate 7z format, but apparently not from a stream, as requested by OP.Harry
P
3

Since 7z decompression requires random access, you'll have to read the whole stream into a byte[] and use new SevenZFile(new SeekableInMemoryByteChannel(bytes)). (If it's longer than Integer.MAX_VALUE bytes, you'll have to create a custom SeekableByteChannel implementation.) Once the instance is constructed, the process is the same as in SANN3's answer.

If it won't fit in memory and you can't write it to a temporary file, then 7zip isn't a suitable compression algorithm given its need for random access.

Paraldehyde answered 12/7, 2018 at 22:33 Comment(0)
C
0

Code working for me :

pom.xml to add :

<!-- 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>

Add to class

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();
}

UPDATE :

Without store :

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);
    }
}

With ls method :

    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;
    }

And with getContent method :

   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;
    }
Caribou answered 4/11, 2020 at 11:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.