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

Community
  • 1
  • 1
ali molaie
  • 21
  • 3

2 Answers2

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.
}
weston
  • 54,145
  • 21
  • 145
  • 203
  • please write me a Example of Streams – ali molaie Dec 15 '16 at 22:41
  • well it's pretty similar to what I have written actually, http://stackoverflow.com/a/40019374/360211 unless you have java 7 http://stackoverflow.com/a/19194580/360211 – weston Dec 15 '16 at 22:45
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);
            }
        }
    }
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • tanks - you save my time - what is best buffer size for all old and new devices ?? – ali molaie Dec 15 '16 at 23:48
  • I read in the comments here: http://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 Dec 16 '16 at 02:10
  • @weston The `InputStream` contract for [`read(byte[] b)`](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html#read-byte:A-) 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 Dec 16 '16 at 05: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 Dec 16 '16 at 13:51
  • @weston It does block. The single-byte [`read()`](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html#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 Dec 16 '16 at 15:57