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