【问题标题】:How to unzip files programmatically in Android?如何在 Android 中以编程方式解压缩文件?
【发布时间】:2011-03-23 22:01:27
【问题描述】:

我需要一个小代码 sn-p 从给定的 .zip 文件中解压缩几个文件,并根据它们在压缩文件中的格式给出单独的文件。请发布您的知识并帮助我。

【问题讨论】:

标签: java android unzip


【解决方案1】:

Android 有内置的 Java API。查看java.util.zip 包。

ZipInputStream 类是你应该研究的。从 ZipInputStream 中读取 ZipEntry 并将其转储到文件系统/文件夹中。检查similar example to compress into zip文件。

【讨论】:

  • 您应该提供了一个代码示例。你错过了很多积分。
【解决方案2】:

这是我使用的解压方法:

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

【讨论】:

  • 您认为相同的代码可以用于解压或解压扩展 APK 扩展文件 obb 文件吗?
【解决方案3】:

这是一个 ZipFileIterator(类似于 java 迭代器,但用于 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
    }
    }
}

【讨论】:

  • 您认为相同的代码可以用于解压或解压扩展 APK 扩展文件 obb 文件吗?
【解决方案4】:

对 peno 的版本进行了一些优化。性能的提升是显而易见的。

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

【讨论】:

  • 我认为是的,它有效,因为它是非常常用的拆包方式。只需设法获得正确的“路径”和“zipname”。我还看到了一些你可能感兴趣的东西(当然你已经看过了):link
  • 因为如果您的ze 是一个目录,您需要跳过“仅文件”操作。尝试执行这些操作会导致异常。
  • 这个答案不应该工作,因为它不会创建丢失的文件来写入数据!!
  • 实际上,如果zip文件是在没有垃圾路径的情况下创建的,则此代码将不起作用,例如,您可以运行此代码来解压缩一个APK文件,您会得到FileNotFoundException。
【解决方案5】:

虽然这里已有的答案运行良好,但我发现它们比我希望的要慢一些。相反,我使用了zip4j,我认为这是最好的解决方案,因为它的速度很快。它还允许不同的压缩量选项,我发现这很有用。

【讨论】:

    【解决方案6】:
    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 类(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 布局(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>
    

    ->Menifest 文件中的权限:

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

    【讨论】:

      【解决方案7】:

      根据 Vasily Sochinsky 的回答稍作调整和小修复:

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

      显着差异

      • public static - 这是一个可以在任何地方使用的静态实用方法。
      • 2 File 参数,因为String 是:/ 用于文件,并且之前无法指定要提取 zip 文件的位置。还有path + filename 串联 > https://stackoverflow.com/a/412495/995891
      • throws - 因为 catch late - 如果真的对它们不感兴趣,请添加 try catch。
      • 实际上确保在所有情况下都存在所需的目录。并非每个 zip 都在文件条目之前包含所有必需的目录条目。这有两个潜在的错误:
        • 如果 zip 包含一个空目录,而不是生成的目录有一个现有文件,则忽略此问题。 mkdirs() 的返回值很重要。
        • 在不包含目录的 zip 文件上可能会崩溃。
      • 增加了写入缓冲区的大小,这应该会稍微提高性能。存储通常以 4k 块为单位,以较小的块写入通常比必要的要慢。
      • 使用finally 的魔力来防止资源泄漏。

      所以

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

      应该和原来的一样

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

      【讨论】:

      • 你好,我正在使用像 sdcard/temp/768\769.json 这样的反斜杠获取路径,所以我遇到了错误,你能告诉我如何管理它
      • @AndoMasahashi 应该是 linux 文件系统上的合法文件名。你得到什么错误,文件名最后应该是什么样子?
      • 它看起来像 /sdcard/pictures\picturess.jpeg 和错误文件未找到错误
      • 它工作正常,但是当 zip 中的文件名之一不在UTF8 format 中时会引发异常。所以,我使用了this code,而不是使用apache的commons-compress lib。
      • @AshishTanna 确实,这是一个已知问题blogs.oracle.com/xuemingshen/entry/non_utf_8_encoding_in
      【解决方案8】:

      根据@zapl 的回答,用进度报告解压:

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

      【讨论】:

        【解决方案9】:

        使用下面的类

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

        如何使用

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

        权限

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

        【讨论】:

        • 可以看到文件名,但是在尝试提取文件时,我收到 FileNotFoundException 错误
        【解决方案10】:

        我用来将特定文件从我的 zipfile 解压缩到我的应用程序缓存文件夹中的最小示例。然后我使用不同的方法读取清单文件。

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

        【讨论】:

          【解决方案11】:

          受密码保护的 Zip 文件

          如果你想用密码压缩文件,你可以看看this library,它可以很容易地用密码压缩文件:

          邮编:

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

          解压:

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

          Rar:

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

          这个库的文档已经足够好了,我只是从那里添加了一些示例。 它是完全免费的,专门为 android 编写的。

          【讨论】:

            【解决方案12】:

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

            用法

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

            【讨论】:

            • 它在 'output.outputStream() ' 行抛出一个异常,上面写着“FileNotFoundException ...(不是目录)”
            【解决方案13】:

            我正在处理 Java 的 ZipFile 类无法处理的 zip 文件。 Java 8 显然无法处理压缩方法 12(我相信是 bzip2)。在尝试了包括 zip4j 在内的多种方法后(由于另一个问题,这些特定文件也失败了),我使用支持 additional compression methods as mentioned hereApache 的 commons-compress 取得了成功。

            请注意,下面的 ZipFile 类不是 java.util.zip 中的。

            它实际上是 org.apache.commons.compress.archivers.zip.ZipFile 所以要小心导入。

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

            对于 Gradle:

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

            【讨论】:

              【解决方案14】:

              根据 zapl 的回答,在 Closeable 周围添加 try() 会在使用后自动关闭流。

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

              使用来自 C# .NET 4 Stream.CopyToConstant.DefaultBufferSize (65536) 来自 Jon Skeet 的回答: https://stackoverflow.com/a/411605/1876355

              我总是只看到使用byte[1024]byte[4096] 缓冲区的帖子,从来不知道它可以更大,这可以提高性能并且仍然可以正常工作。

              这里是Stream源代码: 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;
              

              但是,为了安全起见,我拨回了65536,这也是4096 的倍数。

              【讨论】:

              • 这是此线程中的最佳解决方案。此外,我还会在堆栈中使用 BufferedOutputStream 和 FileOutputStream。
              【解决方案15】:

              这里是@arsent 解决方案的更简洁版本:

              fun File.unzip(to: File? = null) {
                  val destinationDir = to ?: File(parentFile, nameWithoutExtension)
                  destinationDir.mkdirs()
              
                  ZipFile(this).use { zipFile ->
                      zipFile
                          .entries()
                          .asSequence()
                          .filter { !it.isDirectory }
                          .forEach { zipEntry ->
                              val currFile = File(destinationDir, zipEntry.name)
                              currFile.parentFile?.mkdirs()
                              zipFile.getInputStream(zipEntry).use { input ->
                                  currFile.outputStream().use { output -> input.copyTo(output) }
                              }
                          }
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-10-24
                • 1970-01-01
                • 2011-11-23
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多