Come decomprimere i file a livello di codice in Android?


131

Ho bisogno di un piccolo frammento di codice che decomprimi alcuni file da un determinato file .zip e fornisca i file separati in base al formato che erano nel file zippato. Per favore pubblica le tue conoscenze e aiutami.


1
È possibile ottenere la soluzione Kotlin qui - stackoverflow.com/a/50990992/1162784
arsent

Risposte:


140

La versione di Peno era stata un po 'ottimizzata. L'aumento delle prestazioni è percepibile.

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
<usi-permesso android: nome = "android.permission.WRITE_EXTERNAL_STORAGE" />
Lou Morda

1
Penso di sì, funziona, perché è un modo molto usuale di spacchettare le cose. Riesci a ottenere il giusto "percorso" e "zipname". Ho anche visto alcune cose che potrebbero interessarti (certo che l'hai già visto): link
Vasily Sochinsky

1
Perché è necessario saltare le operazioni "solo file" se si zeè una directory. Cercare di eseguire queste operazioni provocherà un'eccezione.
Vasily Sochinsky,

1
Questa risposta non dovrebbe funzionare, perché non crea i file mancanti per scrivere i dati su di essa !!
Omar HossamEldin,

1
In realtà, questo codice non funzionerà se il file zip viene creato senza percorso spazzatura, ad esempio, puoi eseguire questo codice per decomprimere un file APK, otterrai FileNotFoundException.
Shaw,

99

Basato sulla risposta di Vasily Sochinsky un po 'ottimizzato e con una piccola correzione:

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

Differenze notevoli

  • public static - questo è un metodo di utilità statica che può essere ovunque.
  • 2 Fileparametri perché Stringsono: / per i file e non è stato possibile specificare dove il file zip deve essere estratto prima. Anche path + filenameconcatenazione> https://stackoverflow.com/a/412495/995891
  • throws- perché cattura in ritardo - aggiungi un tentativo di cattura se davvero non ti interessa.
  • in realtà si assicura che le directory richieste esistano in tutti i casi. Non tutte le zip contengono tutte le voci di directory richieste prima delle voci di file. Questo aveva 2 potenziali bug:
    • se zip contiene una directory vuota e al posto della directory risultante esiste un file esistente, questo è stato ignorato. Il valore di ritorno di mkdirs()è importante.
    • potrebbe bloccarsi su file zip che non contengono directory.
  • aumento della dimensione del buffer di scrittura, questo dovrebbe migliorare un po 'le prestazioni. Lo stoccaggio è di solito in blocchi di 4k e la scrittura in blocchi più piccoli è generalmente più lenta del necessario.
  • usa la magia di finallyper prevenire perdite di risorse.

Così

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

dovrebbe fare l'equivalente dell'originale

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

ciao sto ottenendo il percorso con una barra rovesciata come sdcard / temp / 768 \ 769.json quindi sto ricevendo un errore puoi dirmi come gestirlo
Ando Masahashi,

@AndoMasahashi che dovrebbe essere un nome file legale su un filesystem linux. Che errore ricevi e come dovrebbe apparire il nome del file alla fine?
zapl,

sembra /sdcard/pictures\picturess.jpeg e errore nel file di errore non trovato
Ando Masahashi,

Funziona bene, ma genera un'eccezione quando non è presente uno dei nomi file all'interno di zip UTF8 format. Quindi, ho usato questo codice che utilizza la commons-compresslib di apache .
Ashish Tanna,

@AshishTanna, in effetti, è un problema noto blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
zapl

26

Questo è il mio metodo di decompressione, che uso:

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

pensi che lo stesso codice funzioni per decomprimere o decomprimere i file obb di espansione APK Expansion Files obb?
LOG_TAG


10

La via di 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)
                }
            }
        }
    }

}

uso

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

7

Mentre le risposte che sono già qui funzionano bene, ho scoperto che erano leggermente più lente di quanto sperassi. Invece ho usato zip4j , che penso sia la soluzione migliore per la sua velocità. Ha anche consentito diverse opzioni per la quantità di compressione, che ho trovato utile.


6

AGGIORNAMENTO 2016 utilizzare la seguente classe

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


 }

Come usare

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

permessi

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

posso vedere il nome del file, ma quando provo a eliminare il file, visualizzo l'errore FileNotFoundException
Parth Anjaria,

5

Secondo la risposta di @zapl, decomprimere con il rapporto sullo stato di avanzamento

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

   }

-> layout 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>

-> autorizzazione nel file Menifest:

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

2

Ecco un ZipFileIterator (come un java Iterator, ma per i file 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
    }
    }
}

pensi che lo stesso codice funzioni per decomprimere o decomprimere i file obb di espansione APK Expansion Files obb?
LOG_TAG

2

Esempio minimo che ho usato per decomprimere un file specifico dal mio file zip nella cartella della cache delle mie applicazioni. Ho quindi letto il file manifest usando un metodo diverso.

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

Sto lavorando con file zip che la classe ZipFile di Java non è in grado di gestire. Java 8 apparentemente non riesce a gestire il metodo di compressione 12 (credo che bzip2). Dopo aver provato una serie di metodi tra cui zip4j (che fallisce anche con questi particolari file a causa di un altro problema), ho avuto successo con la compressione comune di Apache che supporta metodi di compressione aggiuntivi come menzionato qui .

Si noti che la classe ZipFile di seguito non è quella di java.util.zip.

In realtà è org.apache.commons.compress.archivers.zip.ZipFile quindi fai attenzione alle importazioni.

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

Per il grado:

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

2

Basato sulla risposta di zapl, l'aggiunta di try()around Closeablechiude automaticamente i flussi dopo l'uso.

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

Utilizzando Constant.DefaultBufferSize( 65536) ottenuto da C# .NET 4 Stream.CopyTo dalla risposta di Jon Skeet qui: https://stackoverflow.com/a/411605/1876355

Vedo sempre solo i post utilizzando byte[1024]o byte[4096]buffer, non ho mai saputo che può essere molto più grande, il che migliora le prestazioni e funziona ancora perfettamente normale.

Ecco il Streamcodice sorgente: 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;

Tuttavia, l'ho ricomposto in base al 65536quale è anche un multiplo 4096solo per sicurezza.


1
Questa è la migliore soluzione in questo thread. Inoltre, utilizzerei anche BufferedOutputStream nello stack con FileOutputStream.
MarkoR

1

File zip protetto da password

se vuoi comprimere i file con password puoi dare un'occhiata a questa libreria che può comprimere facilmente i file con password:

Cerniera lampo:

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

Unzip:

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

Rar:

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

La documentazione di questa libreria è abbastanza buona, ho appena aggiunto alcuni esempi da lì. È totalmente gratuito e scritto appositamente per Android.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.