【问题标题】:How to zip and unzip the files?如何压缩和解压缩文件?
【发布时间】:2020-10-10 23:03:03
【问题描述】:

如何压缩和解压缩 DDMS 中已经存在的文件:data/data/mypackage/files/ 我需要一个简单的例子。我已经搜索了与 zip 和 unzip 相关的内容。但是,没有一个适合我的例子。谁能举个例子。提前谢谢。

【问题讨论】:

    标签: android file zip unzip


    【解决方案1】:

    查看 java.util.zip.* 类的 zip 功能。我已经完成了一些基本的 zip/unzip 代码,我将其粘贴在下面。希望对您有所帮助。

    public static void zip(String[] files, String zipFile) throws IOException {
        BufferedInputStream origin = null;
        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
        try { 
            byte data[] = new byte[BUFFER_SIZE];
    
            for (int i = 0; i < files.length; i++) {
                FileInputStream fi = new FileInputStream(files[i]);    
                origin = new BufferedInputStream(fi, BUFFER_SIZE);
                try {
                    ZipEntry entry = new ZipEntry(files[i].substring(files[i].lastIndexOf("/") + 1));
                    out.putNextEntry(entry);
                    int count;
                    while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
                        out.write(data, 0, count);
                    }
                }
                finally {
                    origin.close();
                }
            }
        }
        finally {
            out.close();
        }
    }
    
    public static void unzip(String zipFile, String location) throws IOException {
        try {
            File f = new File(location);
            if(!f.isDirectory()) {
                f.mkdirs();
            }
            ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile));
            try {
                ZipEntry ze = null;
                while ((ze = zin.getNextEntry()) != null) {
                    String path = location + ze.getName();
    
                    if (ze.isDirectory()) {
                        File unzipFile = new File(path);
                        if(!unzipFile.isDirectory()) {
                            unzipFile.mkdirs();
                        }
                    }
                    else {
                        FileOutputStream fout = new FileOutputStream(path, false);
                        try {
                            for (int c = zin.read(); c != -1; c = zin.read()) {
                                fout.write(c);
                            }
                            zin.closeEntry();
                        }
                        finally {
                            fout.close();
                        }
                    }
                }
            }
            finally {
                zin.close();
            }
        }
        catch (Exception e) {
            Log.e(TAG, "Unzip exception", e);
        }
    }
    

    【讨论】:

    • 这是错误的:String path = location + ze.getName(); if (ze.isDirectory()) { File unzipFile = new File(path); if(!unzipFile.isDirectory()) { unzipFile.mkdirs(); } }
    • 缓冲区大小变量的值应该是多少?
    • 这在 Android 5.1 上运行良好。谢谢!我在这里添加了一个答案,它是您的修改版本,它接受 List&lt;File&gt; filesFile zipFile 作为参数,因为它可能对其他人有帮助。
    【解决方案2】:

    brianestey 提供的 zip 函数运行良好,但由于一次读取一个字节,解压缩函数非常慢。这是他的 unzip 函数的修改版本,它利用缓冲区并且速度更快。

    /**
     * Unzip a zip file.  Will overwrite existing files.
     * 
     * @param zipFile Full path of the zip file you'd like to unzip.
     * @param location Full path of the directory you'd like to unzip to (will be created if it doesn't exist).
     * @throws IOException
     */
    public static void unzip(String zipFile, String location) throws IOException {
        int size;
        byte[] buffer = new byte[BUFFER_SIZE];
    
        try {
            if ( !location.endsWith(File.separator) ) {
                location += File.separator;
            }
            File f = new File(location);
            if(!f.isDirectory()) {
                f.mkdirs();
            }
            ZipInputStream zin = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile), BUFFER_SIZE));
            try {
                ZipEntry ze = null;
                while ((ze = zin.getNextEntry()) != null) {
                    String path = location + ze.getName();
                    File unzipFile = new File(path);
    
                    if (ze.isDirectory()) {
                        if(!unzipFile.isDirectory()) {
                            unzipFile.mkdirs();
                        }
                    } else {
                        // check for and create parent directories if they don't exist
                        File parentDir = unzipFile.getParentFile();
                        if ( null != parentDir ) {
                            if ( !parentDir.isDirectory() ) {
                                parentDir.mkdirs();
                            }
                        }
    
                        // unzip the file
                        FileOutputStream out = new FileOutputStream(unzipFile, false);
                        BufferedOutputStream fout = new BufferedOutputStream(out, BUFFER_SIZE);
                        try {
                            while ( (size = zin.read(buffer, 0, BUFFER_SIZE)) != -1 ) {
                                fout.write(buffer, 0, size);
                            }
    
                            zin.closeEntry();
                        }
                        finally {
                            fout.flush();
                            fout.close();
                        }
                    }
                }
            }
            finally {
                zin.close();
            }
        }
        catch (Exception e) {
            Log.e(TAG, "Unzip exception", e);
        }
    }
    

    【讨论】:

    • 这个解压缩对我不起作用。我收到错误,因为它试图在其父目录之前解压缩文件。在 else 语句中,我添加了一个条件来创建父目录(如果它不存在)。现在可以了。
    • @J-L 感谢您指出这一点。我已经相应地编辑了代码。
    • 真的是throws IOException吗?每个异常都会被catch(Exception e) 捕获。
    • 非常感谢。你节省了我的时间。当ze.getName() 不是直接文件但可能包含该文件夹中的文件夹和文件时,它也很有用。例如1/abc.txt。再次非常感谢您。
    【解决方案3】:

    使用File代替文件路径String

    此答案基于@brianestey's excellent answer

    我已经修改了他的 zip 方法以接受文件列表而不是文件路径和输出 zip 文件而不是文件路径,如果这是他们正在处理的内容,这可能对其他人有帮助。

    public static void zip( List<File> files, File zipFile ) throws IOException {
        final int BUFFER_SIZE = 2048;
    
        BufferedInputStream origin = null;
        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
    
        try {
            byte data[] = new byte[BUFFER_SIZE];
    
            for ( File file : files ) {
                FileInputStream fileInputStream = new FileInputStream( file );
    
                origin = new BufferedInputStream(fileInputStream, BUFFER_SIZE);
    
                String filePath = file.getAbsolutePath();
    
                try {
                    ZipEntry entry = new ZipEntry( filePath.substring( filePath.lastIndexOf("/") + 1 ) );
    
                    out.putNextEntry(entry);
    
                    int count;
                    while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
                        out.write(data, 0, count);
                    }
                }
                finally {
                    origin.close();
                }
            }
        }
        finally {
            out.close();
        }
    }
    

    【讨论】:

      【解决方案4】:

      此答案基于 brianestey、Ben、Giacomo Mattiuzzi、Joshua Pinter。

      重写为 Kotlin 的函数并添加了使用 Uri 的函数。

      import android.content.Context
      import android.net.Uri
      import java.io.*
      import java.util.zip.ZipEntry
      import java.util.zip.ZipInputStream
      import java.util.zip.ZipOutputStream
      
      private const val MODE_WRITE = "w"
      private const val MODE_READ = "r"
      
      fun zip(zipFile: File, files: List<File>) {
          ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))).use { outStream ->
              zip(outStream, files)
          }
      }
      
      fun zip(context: Context, zipFile: Uri, files: List<File>) {
          context.contentResolver.openFileDescriptor(zipFile, MODE_WRITE).use { descriptor ->
              descriptor?.fileDescriptor?.let {
                  ZipOutputStream(BufferedOutputStream(FileOutputStream(it))).use { outStream ->
                      zip(outStream, files)
                  }
              }
          }
      }
      
      private fun zip(outStream: ZipOutputStream, files: List<File>) {
          files.forEach { file ->
              outStream.putNextEntry(ZipEntry(file.name))
              BufferedInputStream(FileInputStream(file)).use { inStream ->
                  inStream.copyTo(outStream)
              }
          }
      }
      
      fun unzip(zipFile: File, location: File) {
          ZipInputStream(BufferedInputStream(FileInputStream(zipFile))).use { inStream ->
              unzip(inStream, location)
          }
      }
      
      fun unzip(context: Context, zipFile: Uri, location: File) {
          context.contentResolver.openFileDescriptor(zipFile, MODE_READ).use { descriptor ->
              descriptor?.fileDescriptor?.let {
                  ZipInputStream(BufferedInputStream(FileInputStream(it))).use { inStream ->
                      unzip(inStream, location)
                  }
              }
          }
      }
      
      private fun unzip(inStream: ZipInputStream, location: File) {
          if (location.exists() && !location.isDirectory)
              throw IllegalStateException("Location file must be directory or not exist")
      
          if (!location.isDirectory) location.mkdirs()
      
          val locationPath = location.absolutePath.let {
              if (!it.endsWith(File.separator)) "$it${File.separator}"
              else it
          }
      
          var zipEntry: ZipEntry?
          var unzipFile: File
          var unzipParentDir: File?
      
          while (inStream.nextEntry.also { zipEntry = it } != null) {
              unzipFile = File(locationPath + zipEntry!!.name)
              if (zipEntry!!.isDirectory) {
                  if (!unzipFile.isDirectory) unzipFile.mkdirs()
              } else {
                  unzipParentDir = unzipFile.parentFile
                  if (unzipParentDir != null && !unzipParentDir.isDirectory) {
                      unzipParentDir.mkdirs()
                  }
                  BufferedOutputStream(FileOutputStream(unzipFile)).use { outStream ->
                      inStream.copyTo(outStream)
                  }
              }
          }
      }
      

      【讨论】: