Comment décompresser des fichiers par programmation sous Android?


131

J'ai besoin d'un petit extrait de code qui décompresse quelques fichiers d'un fichier .zip donné et donne les fichiers séparés en fonction du format dans lequel ils se trouvaient dans le fichier compressé. Veuillez poster vos connaissances et m'aider.


1
Vous pouvez obtenir la solution Kotlin ici - stackoverflow.com/a/50990992/1162784
arsent le

Réponses:


140

Avait un peu optimisé la version de peno. L'augmentation des performances est perceptible.

private boolean unpackZip(String path, String zipname)
{       
     InputStream is;
     ZipInputStream zis;
     try 
     {
         String filename;
         is = new FileInputStream(path + zipname);
         zis = new ZipInputStream(new BufferedInputStream(is));          
         ZipEntry ze;
         byte[] buffer = new byte[1024];
         int count;

         while ((ze = zis.getNextEntry()) != null) 
         {
             filename = ze.getName();

             // Need to create directories if not exists, or
             // it will generate an Exception...
             if (ze.isDirectory()) {
                File fmd = new File(path + filename);
                fmd.mkdirs();
                continue;
             }

             FileOutputStream fout = new FileOutputStream(path + filename);

             while ((count = zis.read(buffer)) != -1) 
             {
                 fout.write(buffer, 0, count);             
             }

             fout.close();               
             zis.closeEntry();
         }

         zis.close();
     } 
     catch(IOException e)
     {
         e.printStackTrace();
         return false;
     }

    return true;
}

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

1
Je pense que oui, cela fonctionne, car c'est une manière très habituelle de déballer les choses. Arrivez simplement à obtenir le bon «chemin» et le «nom zip». J'ai aussi vu des choses qui pourraient vous intéresser (vous l'avez certainement déjà vu): lien
Vasily Sochinsky

1
Parce que vous devez ignorer les opérations "fichier uniquement" si vous êtes zeun répertoire. Essayer d'effectuer ces opérations provoquera une exception.
Vasily Sochinsky

1
Cette réponse ne devrait pas fonctionner, car elle ne crée pas les fichiers manquants pour y écrire des données !!
Omar HossamEldin

1
En fait, ce code ne fonctionnera pas si le fichier zip est créé sans chemin indésirable, par exemple, vous pouvez exécuter ce code pour décompresser un fichier APK, vous obtiendrez FileNotFoundException.
Shaw

99

Basé sur la réponse de Vasily Sochinsky un peu modifiée et avec une petite correction:

public static void unzip(File zipFile, File targetDirectory) throws IOException {
    ZipInputStream zis = new ZipInputStream(
            new BufferedInputStream(new FileInputStream(zipFile)));
    try {
        ZipEntry ze;
        int count;
        byte[] buffer = new byte[8192];
        while ((ze = zis.getNextEntry()) != null) {
            File file = new File(targetDirectory, ze.getName());
            File dir = ze.isDirectory() ? file : file.getParentFile();
            if (!dir.isDirectory() && !dir.mkdirs())
                throw new FileNotFoundException("Failed to ensure directory: " +
                        dir.getAbsolutePath());
            if (ze.isDirectory())
                continue;
            FileOutputStream fout = new FileOutputStream(file);
            try {
                while ((count = zis.read(buffer)) != -1)
                    fout.write(buffer, 0, count);
            } finally {
                fout.close();
            }
            /* if time should be restored as well
            long time = ze.getTime();
            if (time > 0)
                file.setLastModified(time);
            */
        }
    } finally {
        zis.close();
    }
}

Différences notables

  • public static - c'est une méthode utilitaire statique qui peut être n'importe où.
  • 2 Fileparamètres car Stringsont: / pour les fichiers et on n'a pas pu spécifier où le fichier zip doit être extrait avant. Aussi path + filenameconcaténation> https://stackoverflow.com/a/412495/995891
  • throws- parce que la capture tardive - ajoutez une capture d'essai si cela ne vous intéresse vraiment pas.
  • s'assure effectivement que les répertoires requis existent dans tous les cas. Tous les fichiers zip ne contiennent pas toutes les entrées de répertoire requises avant les entrées de fichier. Cela avait 2 bogues potentiels:
    • si le zip contient un répertoire vide et qu'au lieu du répertoire résultant il y a un fichier existant, cela a été ignoré. La valeur de retour de mkdirs()est importante.
    • pourrait planter sur les fichiers zip qui ne contiennent pas de répertoires.
  • augmentation de la taille du tampon d'écriture, cela devrait améliorer un peu les performances. Le stockage se fait généralement en blocs de 4 Ko et l'écriture en petits morceaux est généralement plus lente que nécessaire.
  • utilise la magie de finallypour éviter les fuites de ressources.

Alors

unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));

devrait faire l'équivalent de l'original

unpackZip("/sdcard/", "pictures.zip")

bonjour je reçois un chemin avec une barre oblique inverse comme sdcard / temp / 768 \ 769.json donc je reçois une erreur, pouvez-vous me dire comment le gérer
Ando Masahashi

@AndoMasahashi qui devrait être un nom de fichier légal sur un système de fichiers Linux. Quelle erreur obtenez-vous et à quoi devrait ressembler le nom de fichier à la fin?
zapl

il ressemble à /sdcard/pictures\picturess.jpeg et fichier d'erreur introuvable erreur
Ando Masahashi

Cela fonctionne bien, mais il lève une exception lorsque l'un des noms de fichier à l'intérieur de zip n'est pas UTF8 format. Donc, j'ai utilisé ce code à la place qui utilise la librairie d'Apache commons-compress.
Ashish Tanna

@AshishTanna en effet, c'est un problème connu blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
zapl

26

Voici ma méthode de décompression, que j'utilise:

private boolean unpackZip(String path, String zipname)
{       
     InputStream is;
     ZipInputStream zis;
     try 
     {
         is = new FileInputStream(path + zipname);
         zis = new ZipInputStream(new BufferedInputStream(is));          
         ZipEntry ze;

         while((ze = zis.getNextEntry()) != null) 
         {
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             byte[] buffer = new byte[1024];
             int count;

             String filename = ze.getName();
             FileOutputStream fout = new FileOutputStream(path + filename);

             // reading and writing
             while((count = zis.read(buffer)) != -1) 
             {
                 baos.write(buffer, 0, count);
                 byte[] bytes = baos.toByteArray();
                 fout.write(bytes);             
                 baos.reset();
             }

             fout.close();               
             zis.closeEntry();
         }

         zis.close();
     } 
     catch(IOException e)
     {
         e.printStackTrace();
         return false;
     }

    return true;
}

pensez-vous que le même code fonctionne pour décompresser ou décompresser les fichiers obb d'extension APK Expansion Files?
LOG_TAG


10

La voie Kotlin

//FileExt.kt

data class ZipIO (val entry: ZipEntry, val output: File)

fun File.unzip(unzipLocationRoot: File? = null) {

    val rootFolder = unzipLocationRoot ?: File(parentFile.absolutePath + File.separator + nameWithoutExtension)
    if (!rootFolder.exists()) {
       rootFolder.mkdirs()
    }

    ZipFile(this).use { zip ->
        zip
        .entries()
        .asSequence()
        .map {
            val outputFile = File(rootFolder.absolutePath + File.separator + it.name)
            ZipIO(it, outputFile)
        }
        .map {
            it.output.parentFile?.run{
                if (!exists()) mkdirs()
            }
            it
        }
        .filter { !it.entry.isDirectory }
        .forEach { (entry, output) ->
            zip.getInputStream(entry).use { input ->
                output.outputStream().use { output ->
                    input.copyTo(output)
                }
            }
        }
    }

}

Usage

val zipFile = File("path_to_your_zip_file")
file.unzip()

7

Bien que les réponses qui sont déjà ici fonctionnent bien, j'ai trouvé qu'elles étaient légèrement plus lentes que ce que j'avais espéré. Au lieu de cela, j'ai utilisé zip4j , qui est à mon avis la meilleure solution en raison de sa vitesse. Cela permettait également différentes options pour la quantité de compression, ce que j'ai trouvé utile.


6

UPDATE 2016 utilise la classe suivante

    package com.example.zip;

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


 }

Comment utiliser

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

Autorisations

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

peut voir le nom du fichier, mais en essayant d'extraire le fichier, j'obtiens une erreur FileNotFoundException
Parth Anjaria

5

Selon la réponse @zapl, Décompressez avec le rapport d'avancement:

public interface UnzipFile_Progress
{
    void Progress(int percent, String FileName);
}

// unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));
public static void UnzipFile(File zipFile, File targetDirectory, UnzipFile_Progress progress) throws IOException,
        FileNotFoundException
{
    long total_len = zipFile.length();
    long total_installed_len = 0;

    ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
    try
    {
        ZipEntry ze;
        int count;
        byte[] buffer = new byte[1024];
        while ((ze = zis.getNextEntry()) != null)
        {
            if (progress != null)
            {
                total_installed_len += ze.getCompressedSize();
                String file_name = ze.getName();
                int percent = (int)(total_installed_len * 100 / total_len);
                progress.Progress(percent, file_name);
            }

            File file = new File(targetDirectory, ze.getName());
            File dir = ze.isDirectory() ? file : file.getParentFile();
            if (!dir.isDirectory() && !dir.mkdirs())
                throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
            if (ze.isDirectory())
                continue;
            FileOutputStream fout = new FileOutputStream(file);
            try
            {
                while ((count = zis.read(buffer)) != -1)
                    fout.write(buffer, 0, count);
            } finally
            {
                fout.close();
            }

            // if time should be restored as well
            long time = ze.getTime();
            if (time > 0)
                file.setLastModified(time);
        }
    } finally
    {
        zis.close();
    }
}

3
public class MainActivity extends Activity {

private String LOG_TAG = MainActivity.class.getSimpleName();

private File zipFile;
private File destination;

private TextView status;

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

    status = (TextView) findViewById(R.id.main_status);
    status.setGravity(Gravity.CENTER);

    if ( initialize() ) {
        zipFile = new File(destination, "BlueBoxnew.zip");
        try {
            Unzipper.unzip(zipFile, destination);
            status.setText("Extracted to \n"+destination.getAbsolutePath());
        } catch (ZipException e) {
            Log.e(LOG_TAG, e.getMessage());
        } catch (IOException e) {
            Log.e(LOG_TAG, e.getMessage());
        }
    } else {
        status.setText("Unable to initialize sd card.");
    }
}

public boolean initialize() {
    boolean result = false;
     File sdCard = new File(Environment.getExternalStorageDirectory()+"/zip/");
    //File sdCard = Environment.getExternalStorageDirectory();
    if ( sdCard != null ) {
        destination = sdCard;
        if ( !destination.exists() ) {
            if ( destination.mkdir() ) {
                result = true;
            }
        } else {
            result = true;
        }
    }

    return result;
}

 }

-> Classe d'assistance (Unzipper.java)

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

   public class Unzipper {

private static String LOG_TAG = Unzipper.class.getSimpleName();

public static void unzip(final File file, final File destination) throws ZipException, IOException {
    new Thread() {
        public void run() {
            long START_TIME = System.currentTimeMillis();
            long FINISH_TIME = 0;
            long ELAPSED_TIME = 0;
            try {
                ZipInputStream zin = new ZipInputStream(new FileInputStream(file));
                String workingDir = destination.getAbsolutePath()+"/";

                byte buffer[] = new byte[4096];
                int bytesRead;
                ZipEntry entry = null;
                while ((entry = zin.getNextEntry()) != null) {
                    if (entry.isDirectory()) {
                        File dir = new File(workingDir, entry.getName());
                        if (!dir.exists()) {
                            dir.mkdir();
                        }
                        Log.i(LOG_TAG, "[DIR] "+entry.getName());
                    } else {
                        FileOutputStream fos = new FileOutputStream(workingDir + entry.getName());
                        while ((bytesRead = zin.read(buffer)) != -1) {
                            fos.write(buffer, 0, bytesRead);
                        }
                        fos.close();
                        Log.i(LOG_TAG, "[FILE] "+entry.getName());
                    }
                }
                zin.close();

                FINISH_TIME = System.currentTimeMillis();
                ELAPSED_TIME = FINISH_TIME - START_TIME;
                Log.i(LOG_TAG, "COMPLETED in "+(ELAPSED_TIME/1000)+" seconds.");
            } catch (Exception e) {
                Log.e(LOG_TAG, "FAILED");
            }
        };
    }.start();
}

   }

-> mise en page xml (activity_main.xml):

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
 android:layout_height="match_parent"
 tools:context=".MainActivity" >

<TextView
    android:id="@+id/main_status"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    android:text="@string/hello_world" />

</RelativeLayout>

-> permission dans le fichier Menifest:

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

2

Voici un ZipFileIterator (comme un java Iterator, mais pour les fichiers zip):

package ch.epfl.bbp.io;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ZipFileIterator implements Iterator<File> {

    private byte[] buffer = new byte[1024];

    private FileInputStream is;
    private ZipInputStream zis;
    private ZipEntry ze;

    public ZipFileIterator(File file) throws FileNotFoundException {
    is = new FileInputStream(file);
    zis = new ZipInputStream(new BufferedInputStream(is));
    }

    @Override
    public boolean hasNext() {
    try {
        return (ze = zis.getNextEntry()) != null;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
    }

    @Override
    public File next() {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int count;

        String filename = ze.getName();
        File tmpFile = File.createTempFile(filename, "tmp");
        tmpFile.deleteOnExit();// TODO make it configurable
        FileOutputStream fout = new FileOutputStream(tmpFile);

        while ((count = zis.read(buffer)) != -1) {
        baos.write(buffer, 0, count);
        byte[] bytes = baos.toByteArray();
        fout.write(bytes);
        baos.reset();
        }
        fout.close();
        zis.closeEntry();

        return tmpFile;

    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    }

    @Override
    public void remove() {
    throw new RuntimeException("not implemented");
    }

    public void close() {
    try {
        zis.close();
        is.close();
    } catch (IOException e) {// nope
    }
    }
}

pensez-vous que le même code fonctionne pour décompresser ou décompresser les fichiers obb d'extension APK Expansion Files?
LOG_TAG

2

Exemple minimal que j'ai utilisé pour décompresser un fichier spécifique de mon fichier zip dans le dossier de cache de mes applications. J'ai ensuite lu le fichier manifeste en utilisant une méthode différente.

private void unzipUpdateToCache() {
    ZipInputStream zipIs = new ZipInputStream(context.getResources().openRawResource(R.raw.update));
    ZipEntry ze = null;

    try {

        while ((ze = zipIs.getNextEntry()) != null) {
            if (ze.getName().equals("update/manifest.json")) {
                FileOutputStream fout = new FileOutputStream(context.getCacheDir().getAbsolutePath() + "/manifest.json");

                byte[] buffer = new byte[1024];
                int length = 0;

                while ((length = zipIs.read(buffer))>0) {
                    fout.write(buffer, 0, length);
                }
                zipIs .closeEntry();
                fout.close();
            }
        }
        zipIs .close();

    } catch (IOException e) {
        e.printStackTrace();
    }

}

2

Je travaille avec des fichiers zip que la classe ZipFile de Java n'est pas capable de gérer. Java 8 ne peut apparemment pas gérer la méthode de compression 12 (bzip2 je crois). Après avoir essayé un certain nombre de méthodes, y compris zip4j (qui échoue également avec ces fichiers particuliers en raison d'un autre problème), j'ai eu du succès avec la compression commune d' Apache qui prend en charge des méthodes de compression supplémentaires comme mentionné ici .

Notez que la classe ZipFile ci-dessous n'est pas celle de java.util.zip.

C'est en fait org.apache.commons.compress.archivers.zip.ZipFile donc soyez prudent avec les importations.

try (ZipFile zipFile = new ZipFile(archiveFile)) {
    Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
    while (entries.hasMoreElements()) {
        ZipArchiveEntry entry = entries.nextElement();
        File entryDestination = new File(destination, entry.getName());
        if (entry.isDirectory()) {
            entryDestination.mkdirs();
        } else {
            entryDestination.getParentFile().mkdirs();
            try (InputStream in = zipFile.getInputStream(entry); OutputStream out = new FileOutputStream(entryDestination)) {
                IOUtils.copy(in, out);
            }
        }
    }
} catch (IOException ex) {
    log.debug("Error unzipping archive file: " + archiveFile, ex);
}

Pour Gradle:

compile 'org.apache.commons:commons-compress:1.18'

2

Basé sur la réponse de zapl, l'ajout de try()around Closeableferme les flux automatiquement après utilisation.

public static void unzip(File zipFile, File targetDirectory) {
    try (FileInputStream fis = new FileInputStream(zipFile)) {
        try (BufferedInputStream bis = new BufferedInputStream(fis)) {
            try (ZipInputStream zis = new ZipInputStream(bis)) {
                ZipEntry ze;
                int count;
                byte[] buffer = new byte[Constant.DefaultBufferSize];
                while ((ze = zis.getNextEntry()) != null) {
                    File file = new File(targetDirectory, ze.getName());
                    File dir = ze.isDirectory() ? file : file.getParentFile();
                    if (!dir.isDirectory() && !dir.mkdirs())
                        throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
                    if (ze.isDirectory())
                        continue;
                    try (FileOutputStream fout = new FileOutputStream(file)) {
                        while ((count = zis.read(buffer)) != -1)
                            fout.write(buffer, 0, count);
                    }
                }
            }
        }
    } catch (Exception ex) {
        //handle exception
    }
}

Utilisation Constant.DefaultBufferSize( 65536) obtenu de C# .NET 4 Stream.CopyTo de la réponse de Jon Skeet ici: https://stackoverflow.com/a/411605/1876355

Je vois toujours juste des messages en utilisant byte[1024]ou en byte[4096]tampon, je n'ai jamais su que cela pouvait être beaucoup plus volumineux, ce qui améliore les performances et fonctionne toujours parfaitement.

Voici le Streamcode source: https://referencesource.microsoft.com/#mscorlib/system/io/stream.cs

//We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.

private const int _DefaultCopyBufferSize = 81920;

Cependant, je l'ai recomposé, ce 65536qui est également un multiple de 4096juste pour être sûr.


1
C'est la meilleure solution dans ce fil. En outre, j'utiliserais également BufferedOutputStream en pile avec FileOutputStream.
MarkoR

1

Fichier Zip protégé par mot de passe

si vous souhaitez compresser des fichiers avec un mot de passe, vous pouvez jeter un œil à cette bibliothèque qui peut facilement compresser les fichiers avec un mot de passe:

Zip *: français:

ZipArchive zipArchive = new ZipArchive();
zipArchive.zip(targetPath,destinationPath,password);

Décompressez:

ZipArchive zipArchive = new ZipArchive();
zipArchive.unzip(targetPath,destinationPath,password);

Rar:

RarArchive rarArchive = new RarArchive();
rarArchive.extractArchive(file archive, file destination);

La documentation de cette bibliothèque est assez bonne, je viens d'en ajouter quelques exemples. C'est totalement gratuit et spécialement écrit pour Android.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.