Android – Como descomprimir un archivo zip

En este ejemplo veremos como descomprimir un archivo zip en Android.

La descompresión de archivos zip en Android no es diferente a la descompresión que habitualmente realizamos con cualquier aplicación en Java. Solo hay que tener cuidado en ubicar las carpetas origen del archivo y destino para la descompresión y en solicitar los permisos necesarios.

En este ejemplo suponemos que nuestro archivo zip estará ubicado en la carpeta publica Download y que queremos descomprimir su contenido en la carpeta privada de Documents de tu aplicación.

Cómo acceder a la carpeta pública download y a la carpeta privada ‘documents’ de la aplicación

  • Carpeta Publica getExternalStoragePublicDirectory(carpeta_tipo)
  • Carpeta Privada de tu app getExternalFilesDir(carpeta_tipo)

Nos queda de este modo:

// archivo zip en la carpeta download del sistema
File downloadFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File zipFile = new File(downloadFolder, FILE_TEST_ZIP);

// carpeta privada de la app en unidad externa (tarjeta de memoria) dentro 'documents'
File externalFilesDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);

Usamos ZipInputStream para leer el archivo zip y recorrerlo

Luego usaremos una implementación de InputStream ‘ZipInputStream’ para leer el archivo que nos devuelve sucesivamente una entrada del zip

ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {...}

Permisos necesarios para leer y escribir

Para leer y escribir archivos en el almacenamiento externo, tu app debe obtener los permisos de sistema del READ_EXTERNAL_STORAGE o WRITE_EXTERNAL_STORAGE.

A partir de Android 4.4, estos permisos no son necesarios si deseas leer o escribir archivos que solamente son privados para tu app.

A partir de la targetSdkVersion 23 Android 6.0 (API level 23) los permisos deben ser requeridos por nosotros en runtime, en este caso estamos queriendo leer un carpeta del sistema ‘download’ por lo que necesitamos pedirlos en runtime a pesar de que esten declarados en el archivo AndroidManifiest.

Los permisos de WRITE suponen tambien los permisos para READ. A fines de ejemplificar pedimos ambos:

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

Solicitud de permisos en runtime para versiones Android 6 y posteriores:

// verificar si tengo permisos sobre el storage y lo requiero si no lo tengo
        int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // no tenemos permisos... lo pedimos
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSION_CODE);
        } else {
           // ok process ....
        }

También es muy conveniente que verifiques antes de escribir si el almacenamiento esta disponible. Te dejo este post en el que te explico como verificar si el almacenamiento externo esta disponible

Veamos como queda el código completo

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getName();
    private static final String FILE_TEST_ZIP = "fileTest.zip";
    private static final int REQUEST_PERMISSION_CODE = 5656;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void unzip(View view) {

        verifyStoragePermissions();

    }

    private void processZip() {

        // Zip file in the system download folder
        File downloadFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        File zipFile = new File(downloadFolder, FILE_TEST_ZIP);

        // Private app folder on external drive (memory card) inside 'documents'
        File externalFilesDir = getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS);

        int countFiles = unZip(zipFile, externalFilesDir);

        Toast.makeText(this,"Files unziped " + countFiles, Toast.LENGTH_LONG).show();

    }

    private int unZip(File zipFile, File folderToExtract) {

        if(!zipFile.exists()) {
            Toast.makeText(this,"Zip do not exist ", Toast.LENGTH_SHORT).show();
            return 0;
        }

        try  {

            int totalFilesInZip = countFiles(zipFile);
            int countFiles = 0;

            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));
            ZipEntry zipEntry;
            byte[] buffer = new byte[1024];
            int count;
            while ((zipEntry = zipInputStream.getNextEntry()) != null) {

                if(zipEntry.isDirectory()) {

                    File f = new File(folderToExtract.getPath() + zipEntry.getName());
                    if(!f.isDirectory()) {
                        f.mkdirs();
                    }

                } else {

                    ++countFiles;
                    Log.v(TAG, "Unzipping " + " File " + countFiles + "/" + totalFilesInZip + " Name: " + zipEntry.getName());

                    File fileOut = new File(folderToExtract, zipEntry.getName());
                    FileOutputStream fileOutputStream = new FileOutputStream(fileOut);
                    while ((count = zipInputStream.read(buffer)) != -1) {
                        fileOutputStream.write(buffer, 0, count);
                    }

                    zipInputStream.closeEntry();
                    fileOutputStream.close();
                }

            }
            zipInputStream.close();

            return countFiles;

        } catch(Exception e) {
            Log.e(TAG, "Unzip file " + zipFile.getName(), e);
            return 0;
        }

    }

    // to show progress
    private int countFiles(File zipFile) throws IOException {
        ZipFile xzipFile = new ZipFile(zipFile);
        final Enumeration<? extends ZipEntry> entries = xzipFile.entries();
        int numRegularFiles = 0;
        while (entries.hasMoreElements()) {
            if (! entries.nextElement().isDirectory()) {
                ++numRegularFiles;
            }
        }
        return numRegularFiles;
    }

    // verify permission
    private void verifyStoragePermissions() {

        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    REQUEST_PERMISSION_CODE);
        } else {

            processZip();

        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_PERMISSION_CODE: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    // permission was granted! Do the work unzip!!
                    processZip();

                } else {
                    Toast.makeText(this, "I can't unzip. Please give me permissions", Toast.LENGTH_LONG).show();
                }
                return;
            }
        }
    }

}

Descarga este código completo

Hi! If you find my posts helpful, please support me by inviting me for a coffee :)

Ver también