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.

user3330817
  • 181
  • 1
  • 1
  • 3
  • 1
    Take a look at the [Apache Commons, Compress](https://commons.apache.org/proper/commons-compress/) library which I believe has a 7z implementation – MadProgrammer Feb 20 '14 at 03:11

3 Answers3

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();
SANN3
  • 9,459
  • 6
  • 61
  • 97
  • 1
    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. – user3330817 Mar 07 '14 at 08:05
  • 4
    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. – Oleksii K. Dec 10 '15 at 15:23
  • 2
    It is true that the Commons Compress library can manipulate 7z format, but apparently not from a stream, as requested by OP. – Unda Mar 29 '16 at 10:37
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.

Pr0methean
  • 303
  • 4
  • 14
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;
    }
Thierry
  • 89
  • 1
  • 5