如何在 Android 中以编程方式解压缩文件?

Posted

技术标签:

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

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

【问题讨论】:

您可以在此处获取 Kotlin 解决方案 - ***.com/a/50990992/1162784 【参考方案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_
 android:layout_
 tools:context=".MainActivity" >

<TextView
    android:id="@+id/main_status"
    android:layout_
    android:layout_
    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://***.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 here 的 Apache 的 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.CopyTo 的 Constant.DefaultBufferSize (65536) 来自 Jon Skeet 的回答: https://***.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) 
                
            
    

【讨论】:

以上是关于如何在 Android 中以编程方式解压缩文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ubuntu 14.04 上运行的 ruby​​ 中以编程方式解压缩 .tar.xz 文件(没有中间体)?

在 iOS 中以编程方式压缩和解压缩文件?

以编程方式执行解压缩命令

如何在android中使用XZ lib压缩/解压缩文件

使用 Hadoop 以编程方式解压缩包含多个不相关 csv 文件的文件

如何在mac上正确压缩文件以通过python从s3解压缩?