0

i have a application for extract 7zip file i use this two lib for extract

compile 'org.apache.commons:commons-compress:1.12'

compile files('libs/xz-1.3.jar')

and this is my code

      public void unzip() {
    try  {
        File fc = new File(_location,"Driver2.7z");
        System.out.println("file size"+fc.length());
        //File fd = new File(_location,"Driver2.bin");
        SevenZFile sevenZFile = new SevenZFile(fc);
        SevenZArchiveEntry entry = sevenZFile.getNextEntry();
        System.out.println( "Unzipping " );
        while(entry!=null){
            System.out.println(entry.getName());
            FileOutputStream out = new FileOutputStream(_location + entry.getName());
            //FileOutputStream out = new FileOutputStream(entry.getName());
            System.out.println(entry.getName());
            byte[] content = new byte[(int) entry.getSize()];
            System.out.println(entry.getName());
            sevenZFile.read(content, 0, content.length);
            System.out.println(entry.getName());
            out.write(content);
            System.out.println(entry.getName());
            out.close();
            entry = sevenZFile.getNextEntry();
            System.out.println(entry.getName());
        }
        sevenZFile.close();
   
        Log.d("Unzip", "Unzipping complete. path :  " +_location );
    } catch(Exception e) {
        System.out.println("Decompress"+ "unzip"+ e.getMessage());
    }

}

with this code i get this error on some device

 E/AndroidRuntime: FATAL EXCEPTION: Thread-218
                                                              Process: ir.milano.driver, PID: 30164
                                                              java.lang.OutOfMemoryError: Failed to allocate a 137881308 byte allocation with 4194304 free bytes and 129MB until OOM
                                                                  at com.darkgamers.pepsiman.browser.mainmenu.DecompressFast.unzip(DecompressFast.java:38)
                                                                  at ir.milano.driver.CopyFiles$1.run(CopyFiles.java:75)

the problem is this line

byte[] content = new byte[(int) entry.getSize()];

2
  • 2
    @alfasin Why do you say that?
    – shmosel
    Commented Dec 15, 2016 at 22:31
  • My bad, posted the comment on the wrong post... please ignore!
    – Nir Alfasi
    Commented Dec 15, 2016 at 22:37

2 Answers 2

3

You're trying to extract a large file from the zip, ~130MB. You shouldn't do this by allocating one large contiguous block of bytes. Instead, you should work with Streams.

Streams don't open the whole content at once, rather just keep track of a position into the file allowing reading just part of the file at a time. You already have an output stream FileOutputStream you just need to find the relevant API call to get an input stream and research copying from an input stream to an output.

Alternatively, if no Stream API is provided, you can read and write in multiple chunks using sevenZFile.read but with a much smaller buffer.

byte[] content = new byte[16 * 1024]; //fixed reasonable size buffer
while((int noBytesRead = sevenZFile.read(content, 0, content.size()) != -1){
   //write the noBytesRead from content into your stream.
}
2
1

Don't load the entire Zip entry content into memory. Copy the content in blocks. Buffer size of 4K has been shown to be efficient in tests.

Also, use try-with-resources and enhanced for loop.

byte[] buffer = new byte[4096];
try (SevenZFile sevenZFile = new SevenZFile(fc)) {
    for (SevenZArchiveEntry entry : sevenZFile.getEntries()) {
        try (FileOutputStream out = new FileOutputStream(_location + entry.getName())) {
            for (int len; (len = sevenZFile.read(buffer)) > 0; ) {
                out.write(buffer, 0, len);
            }
        }
    }
}
5
  • tanks - you save my time - what is best buffer size for all old and new devices ??
    – ali molaie
    Commented Dec 15, 2016 at 23:48
  • I read in the comments here: stackoverflow.com/a/43163/360211 that technically read on InputStream contract can return 0 a number of times and is not necessarily done, and -1 is the return value when it's done. That's probably a general situation for input streams that could be network based (for example), but may be worth considering in case code is copied to elsewhere. Assuming sevenZFile is adhering to input stream contracts.
    – weston
    Commented Dec 16, 2016 at 2:10
  • @weston The InputStream contract for read(byte[] b) is that it can never return 0, except if you're an idiot giving it an zero-length array: If the length of b is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to read at least one byte. If no byte is available because the stream is at the end of the file, the value -1 is returned; otherwise, at least one byte is read and stored into b.
    – Andreas
    Commented Dec 16, 2016 at 5:43
  • That's fair enough. Though the contract leaves a gray area where no data is ready, but it's not at the end of the stream either. I guess it must block in such a case.
    – weston
    Commented Dec 16, 2016 at 13:51
  • @weston It does block. The single-byte read() actually says that explicitly: This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.
    – Andreas
    Commented Dec 16, 2016 at 15:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.