How to speed up unzipping time in Java / Android?

2019-01-10 07:38发布

问题:

Unzipping files on android seems to be dreadfully slow. At first I thought this was just the emulator but it appears to be the same on the phone. I've tried different compression levels, and eventually dropped down to storage mode but it still takes ages.

Anyway, there must be a reason! Does anyone else have this problem? My unzip method looks like this:

public void unzip()
{
try{
        FileInputStream fin = new FileInputStream(zipFile);
        ZipInputStream zin = new ZipInputStream(fin);
        File rootfolder = new File(directory);
        rootfolder.mkdirs();
        ZipEntry ze = null;
        while ((ze = zin.getNextEntry())!=null){

            if(ze.isDirectory()){
                dirChecker(ze.getName());
            }
            else{
                FileOutputStream fout = new FileOutputStream(directory+ze.getName());

            for(int c = zin.read();c!=-1;c=zin.read()){
                fout.write(c);
            }
                //Debug.out("Closing streams");
                zin.closeEntry();
                fout.close();

        }
    }
    zin.close();
}
catch(Exception e){
            //Debug.out("Error trying to unzip file " + zipFile);

}
    }

回答1:

I don't know if unzipping on Android is slow, but copying byte for byte in a loop is surely slowing it down even more. Try using BufferedInputStream and BufferedOutputStream - it might be a bit more complicated, but in my experience it is worth it in the end.

BufferedInputStream in = new BufferedInputStream(zin);
BufferedOutputStream out = new BufferedOutputStream(fout);

And then you can write with something like that:

byte b[] = new byte[1024];
int n;
while ((n = in.read(b,0,1024)) >= 0) {
  out.write(b,0,n);
}


回答2:

Thanks for the solution Robert. I modified my unizip method and now it takes only a few secounds instead of 2 minutes. Maybe someone's interested in my solution. So here you go:

public void unzip() {

    try {
        FileInputStream inputStream = new FileInputStream(filePath);
        ZipInputStream zipStream = new ZipInputStream(inputStream);
        ZipEntry zEntry = null;
        while ((zEntry = zipStream.getNextEntry()) != null) {
            Log.d("Unzip", "Unzipping " + zEntry.getName() + " at "
                    + destination);

            if (zEntry.isDirectory()) {
                hanldeDirectory(zEntry.getName());
            } else {
                FileOutputStream fout = new FileOutputStream(
                        this.destination + "/" + zEntry.getName());
                BufferedOutputStream bufout = new BufferedOutputStream(fout);
                byte[] buffer = new byte[1024];
                int read = 0;
                while ((read = zipStream.read(buffer)) != -1) {
                    bufout.write(buffer, 0, read);
                }

                zipStream.closeEntry();
                bufout.close();
                fout.close();
            }
        }
        zipStream.close();
        Log.d("Unzip", "Unzipping complete. path :  " + destination);
    } catch (Exception e) {
        Log.d("Unzip", "Unzipping failed");
        e.printStackTrace();
    }

}

public void hanldeDirectory(String dir) {
        File f = new File(this.destination + dir);
        if (!f.isDirectory()) {
            f.mkdirs();
        }
}


回答3:

Using above ideas and ideas from some other sources I have created this class

Create this new class

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import android.util.Log;

public class DecompressFast {
 private String _zipFile; 
  private String _location; 

  public DecompressFast(String zipFile, String location) { 
    _zipFile = zipFile; 
    _location = location; 
    _dirChecker(""); 
  } 

  public void unzip() { 
    try  { 
      FileInputStream fin = new FileInputStream(_zipFile); 
      ZipInputStream zin = new ZipInputStream(fin); 
      ZipEntry ze = null; 
      while ((ze = zin.getNextEntry()) != null) { 
        Log.v("Decompress", "Unzipping " + ze.getName()); 
        if(ze.isDirectory()) { 
          _dirChecker(ze.getName()); 
        } else { 
          FileOutputStream fout = new FileOutputStream(_location +  ze.getName()); 
          BufferedOutputStream bufout = new BufferedOutputStream(fout);
          byte[] buffer = new byte[1024];
          int read = 0;
          while ((read = zin.read(buffer)) != -1) {
              bufout.write(buffer, 0, read);
          }
          bufout.close();
          zin.closeEntry(); 
          fout.close(); 
        }    
      } 
      zin.close(); 
      Log.d("Unzip", "Unzipping complete. path :  " +_location );
    } catch(Exception e) { 
      Log.e("Decompress", "unzip", e); 
      Log.d("Unzip", "Unzipping failed");
    } 
  } 

  private void _dirChecker(String dir) { 
    File f = new File(_location + dir); 

    if(!f.isDirectory()) { 
      f.mkdirs(); 
    } 
  } 
}

USAGE

just pass your file location of zip file and your destination Location to this class
example

  String zipFile = Environment.getExternalStorageDirectory() + "/the_raven.zip"; //your zip file location
  String unzipLocation = Environment.getExternalStorageDirectory() + "/unzippedtestNew/"; // unzip location
  DecompressFast df= new DecompressFast(zipFile, unzipLocation);
    df.unzip();

Dont Forget to add following permissions in manifest(also Run time permission if version higher than marshmellow)

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

hope this helps



回答4:

The URL that helped me learn how to zip and unzip can be found here.

I used that URL in conjuction with user3203118's answer above for unzipping. This is for future references for people who run in to this issue and need help solving it.

Below is the ZipManager code I am using:

public class ZipManager {

    private static final int BUFFER = 80000;

    public void zip(String[] _files, String zipFileName) {
        try {
            BufferedInputStream origin = null;
            FileOutputStream dest = new FileOutputStream(zipFileName);
            ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
                dest));
            byte data[] = new byte[BUFFER];

            for (int i = 0; i < _files.length; i++) {
                Log.v("Compress", "Adding: " + _files[i]);
                FileInputStream fi = new FileInputStream(_files[i]);
                origin = new BufferedInputStream(fi, BUFFER);

                ZipEntry entry = new ZipEntry(_files[i].substring(_files[i]
                    .lastIndexOf("/") + 1));
                out.putNextEntry(entry);
                int count;

                while ((count = origin.read(data, 0, BUFFER)) != -1) {
                    out.write(data, 0, count);
                }
                origin.close();
            }
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void unzip(String _zipFile, String _targetLocation) {


        // create target location folder if not exist
        dirChecker(_targetLocation);

        try {
            FileInputStream fin = new FileInputStream(_zipFile);
            ZipInputStream zin = new ZipInputStream(fin);
            ZipEntry ze = null;
            while ((ze = zin.getNextEntry()) != null) {

                // create dir if required while unzipping
                if (ze.isDirectory()) {
                    dirChecker(ze.getName());
                } else {
                    FileOutputStream fout = new FileOutputStream(
                    _targetLocation + "/" + ze.getName());
                    BufferedOutputStream bufout = new BufferedOutputStream(fout);
                    byte[] buffer = new byte[1024];
                    int read = 0;
                    while ((read = zin.read(buffer)) != -1) {
                        bufout.write(buffer, 0, read);
                    }

                    zin.closeEntry();
                    bufout.close();
                    fout.close();
                }
            }
            zin.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    private void dirChecker(String dir) {
        File f = new File(dir);
        if (!f.isDirectory()) {
            f.mkdirs();
        }
    }
}


回答5:

Just call this method and it will give you much better performance..

    public boolean unzip(Context context) {
    try {
        FileInputStream fin = new FileInputStream(_zipFile);
        ZipInputStream zin = new ZipInputStream(fin);
        BufferedInputStream in = new BufferedInputStream(zin);
        ZipEntry ze = null;
        while ((ze = zin.getNextEntry()) != null) {
            Log.v("Decompress", "Unzipping " + ze.getName());

            if (ze.isDirectory()) {
                _dirChecker(ze.getName());
            } else {
                FileOutputStream fout = new FileOutputStream(_location
                        + ze.getName());
                    BufferedOutputStream out = new BufferedOutputStream(fout);
                    byte b[] = new byte[1024];
                for (int c = in.read(b,0,1024); c != -1; c = in.read()) {
                    out.write(b,0,c);
                }
                zin.closeEntry();
                fout.close();
            }
        }
        zin.close();
        return true;
    } catch (Exception e) {
        Log.e("Decompress", "unzip", e);
        return false;
    }
}

    private void _dirChecker(String dir) {
    File f = new File(_location + dir);
    if (!f.isDirectory()) {
        f.mkdirs();
    }
}


回答6:

In case of using BufferedOutputStream be sure to flush it. If you do not do it, size smaller than buffer will not be unzipped properly

if (ze.isDirectory()) {
                _dirChecker(ze.getName());
            } else {
                FileOutputStream fout = new FileOutputStream(_location
                        + ze.getName());
                    BufferedOutputStream out = new BufferedOutputStream(fout);
                    byte buffer[] = new byte[1024];
                for (int c = in.read(buffer,0,1024); c != -1; c = in.read()) {
                    out.write(buffer,0,c);
                }
                out.flush();//flush it......
                zin.closeEntry();
                fout.close();
            }