Wie entpacke ich Dateien programmgesteuert in Android?


131

Ich benötige ein kleines Code-Snippet, das einige Dateien aus einer bestimmten ZIP-Datei entpackt und die separaten Dateien entsprechend dem Format der komprimierten Datei angibt. Bitte poste dein Wissen und hilf mir.


1
Sie können die Kotlin-Lösung hier erhalten - stackoverflow.com/a/50990992/1162784
arsent

Antworten:


140

Hatte Penos Version etwas optimiert. Die Leistungssteigerung ist spürbar.

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
<Verwendet-Erlaubnis android: name = "android.permission.WRITE_EXTERNAL_STORAGE" />
Lou Morda

1
Ich denke ja, es funktioniert, weil es sehr üblich ist, Dinge auszupacken. Schaffe es einfach, den richtigen "Pfad" und "Postleitzahl" zu finden. Ich habe auch einige Dinge gesehen, die Sie interessieren könnten (sicher, dass Sie sie bereits gesehen haben): link
Vasily Sochinsky

1
Weil Sie die "Nur-Datei" -Operationen überspringen müssen, wenn Sie zeein Verzeichnis sind. Der Versuch, diese Vorgänge auszuführen, führt zu einer Ausnahme.
Vasily Sochinsky

1
Diese Antwort sollte nicht funktionieren, da sie nicht die fehlenden Dateien erstellt, um Daten darauf zu schreiben !!
Omar HossamEldin

1
Tatsächlich funktioniert dieser Code nicht, wenn die Zip-Datei ohne Junk-Pfad erstellt wird. Sie können diesen Code beispielsweise ausführen, um eine APK-Datei zu entpacken. Sie erhalten FileNotFoundException.
Shaw

99

Basierend auf Vasily Sochinskys Antwort etwas optimiert & mit einer kleinen Korrektur:

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

Bemerkenswerte Unterschiede

  • public static - Dies ist eine statische Dienstprogrammmethode, die überall verwendet werden kann.
  • 2 FileParameter, weil String: / für Dateien und man konnte nicht angeben, wo die Zip-Datei vorher extrahiert werden soll. Auch path + filenameVerkettung> https://stackoverflow.com/a/412495/995891
  • throws- weil spät fangen - fügen Sie einen Versuch fangen, wenn Sie wirklich nicht an ihnen interessiert sind.
  • stellt tatsächlich sicher, dass die erforderlichen Verzeichnisse in allen Fällen vorhanden sind. Nicht jede Zip-Datei enthält alle erforderlichen Verzeichniseinträge vor den Dateieinträgen. Dies hatte 2 mögliche Fehler:
    • Wenn die Zip-Datei ein leeres Verzeichnis enthält und anstelle des resultierenden Verzeichnisses eine vorhandene Datei vorhanden ist, wurde dies ignoriert. Der Rückgabewert vonmkdirs() ist wichtig.
    • könnte bei Zip-Dateien abstürzen, die keine Verzeichnisse enthalten.
  • Wenn die Größe des Schreibpuffers erhöht wird, sollte dies die Leistung etwas verbessern. Die Speicherung erfolgt normalerweise in 4k-Blöcken, und das Schreiben in kleineren Blöcken ist normalerweise langsamer als erforderlich.
  • nutzt die Magie von finally, um Ressourcenlecks zu verhindern.

So

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

sollte das Äquivalent des Originals tun

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

Hallo, ich bekomme einen Pfad mit einem Schrägstrich wie sdcard / temp / 768 \ 769.json, damit ich einen Fehler bekomme. Kannst du mir sagen, wie ich damit
umgehen soll?

@AndoMasahashi das sollte ein legaler Dateiname auf einem Linux-Dateisystem sein. Welchen Fehler bekommen Sie und wie sollte der Dateiname am Ende aussehen?
Zapl

es sieht aus wie /sdcard/pictures\picturess.jpeg und Fehlerdatei nicht gefunden Fehler
Ando Masahashi

Es funktioniert einwandfrei, löst jedoch eine Ausnahme aus, wenn einer der Dateinamen in zip nicht vorhanden ist UTF8 format. Also habe ich stattdessen diesen Code verwendet, der die Bibliothek von Apache verwendet commons-compress.
Ashish Tanna

@ AshishTanna in der Tat, es ist ein bekanntes Problem blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
zapl

26

Dies ist meine Entpackungsmethode, die ich verwende:

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

Denken Sie, dass der gleiche Code zum Entpacken oder Entpacken der APB-Erweiterungsdateien von obb funktioniert?
LOG_TAG


10

Der Kotlin-Weg

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

}

Verwendung

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

7

Während die Antworten, die bereits hier sind, gut funktionieren, stellte ich fest, dass sie etwas langsamer waren, als ich gehofft hatte. Stattdessen habe ich zip4j verwendet , was meiner Meinung nach aufgrund seiner Geschwindigkeit die beste Lösung ist. Es erlaubte auch verschiedene Optionen für das Ausmaß der Komprimierung, was ich nützlich fand.


6

UPDATE 2016 verwendet die folgende Klasse

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


 }

Wie benutzt man

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

Berechtigungen

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

kann den Dateinamen sehen, aber wenn ich versuche, die Datei zu extrahieren, erhalte ich den FileNotFoundException-Fehler
Parth Anjaria

5

Laut @zapl Antwort, Entpacken mit Fortschrittsbericht:

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

 }

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

   }

-> XML-Layout (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>

-> Erlaubnis in der Menifest-Datei:

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

2

Hier ist ein ZipFileIterator (wie ein Java-Iterator, jedoch für Zip-Dateien):

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

Denken Sie, dass der gleiche Code zum Entpacken oder Entpacken der APB-Erweiterungsdateien von obb funktioniert?
LOG_TAG

2

Minimales Beispiel Ich habe eine bestimmte Datei aus meiner Zip-Datei in meinen Anwendungs-Cache-Ordner entpackt. Ich habe dann die Manifestdatei mit einer anderen Methode gelesen.

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

Ich arbeite mit Zip-Dateien, die die ZipFile-Klasse von Java nicht verarbeiten kann. Java 8 kann anscheinend nicht mit der Komprimierungsmethode 12 umgehen (bzip2, glaube ich). Nachdem ich eine Reihe von Methoden ausprobiert hatte, einschließlich zip4j (was auch bei diesen bestimmten Dateien aufgrund eines anderen Problems fehlschlägt), hatte ich Erfolg mit Apaches Commons-Compress, das zusätzliche Komprimierungsmethoden unterstützt , wie hier erwähnt .

Beachten Sie, dass die unten stehende ZipFile-Klasse nicht die aus java.util.zip ist.

Es ist eigentlich org.apache.commons.compress.archivers.zip.ZipFile, also seien Sie vorsichtig mit den Importen.

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

Für Gradle:

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

2

Basierend auf der Antwort von zapl werden durch Hinzufügen von try()Around Closeabledie Streams nach der Verwendung automatisch geschlossen.

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

Verwenden von Constant.DefaultBufferSize( 65536) von C# .NET 4 Stream.CopyTo aus Jon Skeets Antwort hier: https://stackoverflow.com/a/411605/1876355

Ich sehe immer nur Beiträge mit byte[1024]oderbyte[4096] Puffer, wusste nie, dass es viel größer sein kann, was die Leistung verbessert und immer noch ganz normal funktioniert.

Hier ist der StreamQuellcode: 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;

Ich habe es jedoch zurückgerufen, 65536was auch ein Vielfaches von ist, 4096nur um sicher zu gehen.


1
Dies ist die beste Lösung in diesem Thread. Außerdem würde ich BufferedOutputStream im Stack mit FileOutputStream verwenden.
MarkoR

1

Passwortgeschützte Zip-Datei

Wenn Sie Dateien mit einem Kennwort komprimieren möchten, können Sie sich diese Bibliothek ansehen Dateien mit einem Kennwort einfach können:

Postleitzahl:

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

Entpacken:

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

Rar:

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

Die Dokumentation dieser Bibliothek ist gut genug, ich habe nur ein paar Beispiele von dort hinzugefügt. Es ist völlig kostenlos und speziell für Android geschrieben.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.