Как ускорить время распаковки в Java/Android?

Разархивирование файлов на Android кажется ужасно медленным. Сначала я подумал, что это просто эмулятор, но оказалось, что на телефоне то же самое. Я пробовал разные уровни сжатия и в конце концов перешел в режим хранения, но это все равно занимает много времени.

В любом случае должна быть причина! Кто-нибудь еще сталкивался с этой проблемой? Мой метод распаковки выглядит так:

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

}
    }

person digitalWestie    schedule 21.12.2010    source источник
comment
подумайте, где вы распаковываете. Context.getCacheDir() будет внутренним хранилищем, где Environment.getDataDirectory() может быть внутренним (или нет), а Environment.getExternalDirectory() будет на SD-карте. внутренняя память почти наверняка будет быстрее.   -  person Jeffrey Blattman    schedule 22.12.2010


Ответы (6)


Я не знаю, медленная ли распаковка на Android, но копирование байт за байтом в цикле, безусловно, замедляет его еще больше. Попробуйте использовать BufferedInputStream и BufferedOutputStream — это может быть немного сложнее, но, по моему опыту, оно того стоит.

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

И тогда вы можете написать что-то вроде этого:

byte b[] = new byte[1024];
int n;
while ((n = in.read(b,0,1024)) >= 0) {
  out.write(b,0,n);
}
person Robert Kolner    schedule 21.12.2010
comment
Почему бы не поместить BufferedInputStream между FileInputStream и ZipInputStream? То есть, FileInputStream fin = new FileInputStream(zipFile); BufferedInputStream bin = new BufferedInputStream(fin); ZipInputStream zin = new ZipInputStream(bin); - person Dan Breslau; 22.12.2010
comment
прикольно - попробую - person digitalWestie; 22.12.2010
comment
Если операции чтения и записи выполняются в массивах, подобных циклу выборки, то преимущества переноса потоков в буферизованные потоки уменьшаются. Потоки буферизации обеспечивают повышение производительности, когда потребитель/производитель не может работать с байтовыми массивами и выполняет операции побайтно. - person Dilum Ranatunga; 23.04.2012
comment
Вы можете создать BufferedInputStream между FileInputStream и ZipInputStream, и он также работает с лучшей производительностью. - person Ahmed; 28.06.2012
comment
Блестящий. Пошел от около 5 минут на пункт распаковал 5 секунд. Спасатель... спасибо Роберт - person Tim; 16.08.2012
comment
Мой увеличился с 40 минут до 3 секунд. о_О - person Stefan Hoth; 01.11.2012
comment
Дилум Ранатунга был прав, если уже копировать файлы массивами, а не побайтно, буферизованные потоки дают ту же скорость, что я только что проверил. - person fifth; 17.03.2013
comment
Разархивирование 8101 png файлов заняло от 19 минут ‹ 1 мин. Спасибо! - person swebal; 27.06.2013
comment
Как узнать длину массива байтов? - person ThanosFisherman; 14.11.2015
comment
Я проверил и обнаружил, что BufferedOutputStream ускоряет процесс. Кажется, что BufferedInputStream не меняет скорость и может немного замедлить процесс. - person Phuong; 28.12.2015

Спасибо за решение Роберт. Я изменил свой метод распаковки, и теперь это занимает всего несколько секунд вместо 2 минут. Может быть кому-то будет интересно мое решение. Итак, вот:

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();
        }
}
person Thomas Krex    schedule 16.01.2014
comment
Привет, что такое handleDirectory? - person Daniel Gomez Rico; 22.09.2014
comment
О, извините, я проглядел это. Это простой способ создать новую папку в случае, если разархивированный файл является папкой. я добавил выше - person Thomas Krex; 02.10.2014
comment
Как узнать длину массива байтов? (1024) - person ThanosFisherman; 14.11.2015
comment
Извините за поздний ответ. Размер буфера выбирался случайно. Буфер заполняется ZipInputStream и очищается FileOutputStream до тех пор, пока не будут переданы все байты. Таким образом, размер буфера влияет только на количество раз, когда буфер заполняется и опорожняется. Вероятно, есть плюсы и минусы маленьких или больших буферов. Но, честно говоря, я не тратил столько мыслей на это. - person Thomas Krex; 30.03.2016
comment
Это очень быстро :) - person kosemuckel; 18.04.2017
comment
Блестящий. Большое спасибо. Работает как шарм, а скорость как ракета.! - person Ajay Kumar; 08.07.2019
comment
Это очень быстро, @thoxxer Большое спасибо, не могли бы вы объяснить, что именно вы сделали? - person KKSINGLA; 11.03.2021

Используя приведенные выше идеи и идеи из некоторых других источников, я создал этот класс.

Создайте этот новый класс

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

ПРИМЕНЕНИЕ

просто передайте расположение файла zip и место назначения этому классу
примеру

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

Не забудьте добавить следующие разрешения в манифест (также разрешение на выполнение, если версия выше, чем marshmellow)

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

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

надеюсь это поможет

person Manohar    schedule 19.10.2016
comment
Работает как часы! Спасибо чувак! Вы спасли мне всю жизнь! - person Posa; 17.04.2021

URL-адрес, который помог мне научиться архивировать и распаковывать, можно найти здесь.

Я использовал этот URL-адрес в сочетании с ответом пользователя 3203118 выше для распаковки. Это для будущих ссылок для людей, которые сталкиваются с этой проблемой и нуждаются в помощи в ее решении.

Ниже приведен код ZipManager, который я использую:

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();
        }
    }
}
person hafridi    schedule 27.01.2014
comment
Я думаю, что пользователи, у которых также возникла эта проблема, были бы очень рады, если бы вы могли включить код в свой ответ, если это возможно ;-) - person Uli Köhler; 27.01.2014
comment
Я добавил свой почтовый менеджер, который я сейчас использую, и он работает как чемпион. - person hafridi; 11.02.2014
comment
Я получил ошибку из вашего кода, потому что в особой ситуации запись файла шла перед записью папки. Таким образом, запись в файл не удалась. Я ввел новую функцию перед объявлением переменной fout: File f = new File(this.destinationFolder + filePath); File parentDir = f.getParentFile(); if (parentDir!=null) { if (parentDir.isDirectory() == false) parentDir.mkdirs(); } - person seoul; 01.02.2016

Просто вызовите этот метод, и он даст вам гораздо лучшую производительность.

    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();
    }
}
person Deepak Sharma    schedule 13.11.2014

В случае использования BufferedOutputStream обязательно сбросьте его. Если вы этого не сделаете, размер меньше буфера не будет правильно распакован

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();
            }
person crack_head    schedule 21.12.2015
comment
Большое спасибо. Я проголосовал за ваш ответ. Мои аудиофайлы были обрезаны после распаковки. Забыл вызвать out.flush(), как вы сказали - person Nana Ghartey; 11.02.2021