Bagaimana cara unzip file secara terprogram di Android?


131

Saya memerlukan potongan kode kecil yang membuka ritsleting beberapa file dari file .zip yang diberikan dan memberikan file yang terpisah sesuai dengan format mereka di file zip. Silakan kirim pengetahuan Anda dan bantu saya.


1
Anda bisa mendapatkan solusi Kotlin di sini - stackoverflow.com/a/50990992/1162784
arsen

Jawaban:


140

Apakah versi peno dioptimalkan sedikit. Peningkatan kinerja terlihat jelas.

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

1
Saya pikir ya, itu berhasil, karena itu adalah cara yang biasa untuk membongkar barang. Cukup kelola untuk mendapatkan 'path' dan 'zipname' yang benar. Saya juga melihat beberapa hal yang mungkin menarik bagi Anda (yakin Anda sudah melihatnya): tautan
Vasily Sochinsky

1
Karena Anda harus melewati operasi "hanya file" jika Anda zeadalah direktori. Mencoba melakukan operasi ini akan menyebabkan pengecualian.
Vasily Sochinsky

1
Jawaban ini seharusnya tidak berfungsi, karena tidak membuat file yang hilang untuk menulis data !!
Omar HossamEldin

1
Sebenarnya, kode ini tidak akan berfungsi jika file zip dibuat tanpa junk path, misalnya, Anda dapat menjalankan kode ini untuk unzip file APK, Anda akan mendapatkan FileNotFoundException.
Shaw

99

Berdasarkan jawaban Vasily Sochinsky sedikit tweak & dengan perbaikan kecil:

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

Perbedaan yang mencolok

  • public static - ini adalah metode utilitas statis yang bisa di mana saja.
  • 2 Fileparameter karena String: / untuk file dan satu tidak dapat menentukan di mana file zip akan diekstraksi sebelumnya. Juga path + filenameconcatenation> https://stackoverflow.com/a/412495/995891
  • throws- Karena terlambat menangkap - tambahkan try catch jika benar-benar tidak tertarik pada mereka.
  • sebenarnya memastikan bahwa direktori yang diperlukan ada di semua kasus. Tidak setiap zip berisi semua entri direktori yang diperlukan sebelum entri file. Ini memiliki 2 bug potensial:
    • jika zip berisi direktori kosong dan alih-alih direktori yang dihasilkan ada file yang ada, ini diabaikan. Nilai kembalinya mkdirs()penting.
    • bisa macet pada file zip yang tidak mengandung direktori.
  • peningkatan ukuran buffer tulis, ini akan meningkatkan kinerja sedikit. Penyimpanan biasanya dalam blok 4k dan menulis dalam potongan yang lebih kecil biasanya lebih lambat dari yang diperlukan.
  • menggunakan keajaiban finallyuntuk mencegah kebocoran sumber daya.

Begitu

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

harus melakukan yang setara dengan aslinya

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

halo saya mendapatkan jalur dengan garis miring ke belakang seperti sdcard / temp / 768 \ 769.json jadi saya mendapatkan kesalahan, bisakah Anda memberi tahu saya cara mengelolanya
Ando Masahashi

@AndoMasahashi yang seharusnya menjadi nama file legal pada sistem file linux. Apa kesalahan yang Anda dapatkan dan bagaimana seharusnya nama file di akhir?
zapl

sepertinya /sdcard/pictures\picturess.jpeg dan file kesalahan tidak ditemukan kesalahan
Ando Masahashi

Ini berfungsi dengan baik, tapi itu melempar pengecualian ketika salah satu nama file di dalam zip tidak masuk UTF8 format. Jadi, saya menggunakan kode ini sebagai gantinya yang menggunakan commons-compresslib apache .
Ashish Tanna

@AshishTanna memang, itu adalah Masalah yang dikenal blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
zapl

26

Ini adalah metode unzip saya, yang saya gunakan:

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

Menurut Anda apakah kode yang sama berfungsi untuk membuka ritsleting atau membongkar file ekspansi APK File Ekspansi obb?
LOG_TAG


10

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

}

Pemakaian

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

7

Sementara jawaban yang sudah ada di sini berfungsi dengan baik, saya menemukan bahwa mereka sedikit lebih lambat dari yang saya harapkan. Sebaliknya saya menggunakan zip4j , yang menurut saya merupakan solusi terbaik karena kecepatannya. Itu juga memungkinkan untuk opsi berbeda untuk jumlah kompresi, yang menurut saya berguna.


6

UPDATE 2016 menggunakan kelas berikut

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


 }

Cara Penggunaan

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

Izin

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

dapat melihat nama file, tetapi ketika mencoba untuk mengekstraksi file, saya mendapatkan kesalahan FileNotFoundException
Parth Anjaria

5

Menurut jawaban zapl, Unzip dengan laporan kemajuan:

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

 }

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

   }

-> tata letak 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>

-> izin dalam file Menifest:

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

2

Berikut ini adalah ZipFileIterator (seperti java Iterator, tetapi untuk 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
    }
    }
}

Menurut Anda apakah kode yang sama berfungsi untuk membuka ritsleting atau membongkar file ekspansi APK File Ekspansi obb?
LOG_TAG

2

Contoh minimal saya biasa unzip file tertentu dari zipfile saya ke folder cache aplikasi saya. Saya kemudian membaca file manifes menggunakan metode yang berbeda.

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

Saya bekerja dengan file zip yang tidak bisa ditangani oleh kelas ZipFile Java. Java 8 tampaknya tidak dapat menangani metode kompresi 12 (bzip2 saya percaya). Setelah mencoba sejumlah metode termasuk zip4j (yang juga gagal dengan file-file khusus ini karena masalah lain), saya berhasil dengan kompres commons-Apache yang mendukung metode kompresi tambahan seperti yang disebutkan di sini .

Perhatikan bahwa kelas ZipFile di bawah ini bukan yang dari java.util.zip.

Ini sebenarnya org.apache.commons.compress.archivers.zip.ZipFile jadi hati-hati dengan impor.

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

Untuk Gradle:

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

2

Berdasarkan jawaban zapl, menambahkan try()sekitar Closeablemenutup aliran secara otomatis setelah digunakan.

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

Menggunakan Constant.DefaultBufferSize( 65536) didapat dari C# .NET 4 Stream.CopyTo dari jawaban Jon Skeet di sini: https://stackoverflow.com/a/411605/1876355

Saya selalu hanya melihat posting menggunakan byte[1024]atau byte[4096]buffer, tidak pernah tahu itu bisa jauh lebih besar yang meningkatkan kinerja dan masih berfungsi dengan normal.

Berikut ini adalah Streamkode Sumber: 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;

Namun, saya memutarnya kembali 65536yang juga merupakan kelipatan 4096hanya untuk aman.


1
Ini solusi terbaik di utas ini. Selain itu, saya juga akan menggunakan BufferedOutputStream di stack dengan FileOutputStream.
MarkoR

1

File Zip Terlindungi Kata Sandi

jika Anda ingin mengompres file dengan kata sandi Anda dapat melihat di perpustakaan ini yang dapat dengan mudah zip file dengan kata sandi:

Zip:

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

Buka zip:

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

Rar:

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

Dokumentasi perpustakaan ini cukup bagus, saya baru saja menambahkan beberapa contoh dari sana. Ini benar-benar gratis dan ditulis khusus untuk android.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.