【问题标题】:How to unzip files recursively in Java?如何在Java中递归解压缩文件?
【发布时间】:2010-11-02 04:00:56
【问题描述】:

我有一个包含其他 zip 文件的 zip 文件。

例如,邮件文件是abc.zip,它包含xyz.zipclass1.javaclass2.java。而xyz.zip 包含文件class3.javaclass4.java

所以我需要使用 Java 将 zip 文件解压缩到应包含 class1.javaclass2.javaclass3.javaclass4.java 的文件夹中。

【问题讨论】:

  • 这真的会搞砸这个任务:aioobe.org/zip-quine。 (无限递归 zip-in-a-zip-in-a-zip-in-a-..)
  • 只是一个想法,这里提供的所有答案实际上都不起作用,因为您没有考虑嵌套拉链
  • 请注意此问题的答案中的代码,因为它们目前包含安全漏洞。请参阅here 了解更多信息。

标签: java unzip


【解决方案1】:

警告,此处的代码适用于受信任的 zip 文件,写入前没有路径验证,如果您使用它对未知客户端上传的 zip 文件进行压缩,可能会导致zip-slip-vulnerability 中所述的安全漏洞。


此解决方案与之前发布的解决方案非常相似,但此解决方案在解压缩时重新创建了正确的文件夹结构。

public static void extractFolder(String zipFile) throws IOException {
int buffer = 2048;
File file = new File(zipFile);

try (ZipFile zip = new ZipFile(file)) {
  String newPath = zipFile.substring(0, zipFile.length() - 4);

  new File(newPath).mkdir();
  Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();

  // Process each entry
  while (zipFileEntries.hasMoreElements()) {
    // grab a zip file entry
    ZipEntry entry = zipFileEntries.nextElement();
    String currentEntry = entry.getName();
    File destFile = new File(newPath, currentEntry);
    File destinationParent = destFile.getParentFile();

    // create the parent directory structure if needed
    destinationParent.mkdirs();

    if (!entry.isDirectory()) {
      BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
      int currentByte;
      // establish buffer for writing file
      byte[] data = new byte[buffer];

      // write the current file to disk
      FileOutputStream fos = new FileOutputStream(destFile);
      try (BufferedOutputStream dest = new BufferedOutputStream(fos, buffer)) {

        // read and write until last byte is encountered
        while ((currentByte = is.read(data, 0, buffer)) != -1) {
          dest.write(data, 0, currentByte);
        }
        dest.flush();
        is.close();
      }
    }

    if (currentEntry.endsWith(".zip")) {
      // found a zip file, try to open
      extractFolder(destFile.getAbsolutePath());
    }
  }
}

}

【讨论】:

  • 这个功能救了我的命。我们项目中的一些预先编写的 zip 函数根本无法提取文件中的文件夹。这很好用。非常感谢分享。
  • 我很惊讶这个答案没有被接受。无论如何,为你投票,再次感谢。
  • 我知道这是旧的,但是注释掉的行 //destFile = new File(newPath, destFile.getName()); 留在里面有什么意义?
  • @Liam,没有意义。我想我只是在尝试获取当前文件名的不同方法。我决定使用currentEntry 而不是destFile.getName()
  • 这里的代码增加了一个安全漏洞!需要事先验证 zip 文件中的路径。见here
【解决方案2】:

这是一些未经测试的代码,基于我解压缩文件的一些旧代码。

public void doUnzip(String inputZip, String destinationDirectory)
        throws IOException {
    int BUFFER = 2048;
    List zipFiles = new ArrayList();
    File sourceZipFile = new File(inputZip);
    File unzipDestinationDirectory = new File(destinationDirectory);
    unzipDestinationDirectory.mkdir();

    ZipFile zipFile;
    // Open Zip file for reading
    zipFile = new ZipFile(sourceZipFile, ZipFile.OPEN_READ);

    // Create an enumeration of the entries in the zip file
    Enumeration zipFileEntries = zipFile.entries();

    // Process each entry
    while (zipFileEntries.hasMoreElements()) {
        // grab a zip file entry
        ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();

        String currentEntry = entry.getName();

        File destFile = new File(unzipDestinationDirectory, currentEntry);
        destFile = new File(unzipDestinationDirectory, destFile.getName());

        if (currentEntry.endsWith(".zip")) {
            zipFiles.add(destFile.getAbsolutePath());
        }

        // grab file's parent directory structure
        File destinationParent = destFile.getParentFile();

        // create the parent directory structure if needed
        destinationParent.mkdirs();

        try {
            // extract file if not a directory
            if (!entry.isDirectory()) {
                BufferedInputStream is =
                        new BufferedInputStream(zipFile.getInputStream(entry));
                int currentByte;
                // establish buffer for writing file
                byte data[] = new byte[BUFFER];

                // write the current file to disk
                FileOutputStream fos = new FileOutputStream(destFile);
                BufferedOutputStream dest =
                        new BufferedOutputStream(fos, BUFFER);

                // read and write until last byte is encountered
                while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                    dest.write(data, 0, currentByte);
                }
                dest.flush();
                dest.close();
                is.close();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }
    zipFile.close();

    for (Iterator iter = zipFiles.iterator(); iter.hasNext();) {
        String zipName = (String)iter.next();
        doUnzip(
            zipName,
            destinationDirectory +
                File.separatorChar +
                zipName.substring(0,zipName.lastIndexOf(".zip"))
        );
    }

}

【讨论】:

  • 为什么你有一个 throws 声明,但实际上捕获了异常并记录它?这是否意味着调用者会期待永远不会抛出的 IOExceptions..?
  • 感谢您提供这个,非常有用!
  • 如果条目是目录怎么办?
  • @Charlie 我不认为这是真的。如果我使用 PeaZip 提取其中具有目录结构的 zip,则生成的目录将正确重新创建文件夹结构。这种方法(看起来),无论目录结构是什么,都会获取所有文件,并将它们放在基本目标目录中。
  • 删除第 47 行 destFile = new File(unzipDestinationDirectory, destFile.getName());谢谢
【解决方案3】:

我拿 ca.anderson4 并删除 List zipFiles 并重写一点,这就是我得到的:

public class Unzip {

public void unzip(String zipFile) throws ZipException,
        IOException {

    System.out.println(zipFile);;
    int BUFFER = 2048;
    File file = new File(zipFile);

    ZipFile zip = new ZipFile(file);
    String newPath = zipFile.substring(0, zipFile.length() - 4);

    new File(newPath).mkdir();
    Enumeration zipFileEntries = zip.entries();

    // Process each entry
    while (zipFileEntries.hasMoreElements()) {
        // grab a zip file entry
        ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();

        String currentEntry = entry.getName();

        File destFile = new File(newPath, currentEntry);
        destFile = new File(newPath, destFile.getName());
        File destinationParent = destFile.getParentFile();

        // create the parent directory structure if needed
        destinationParent.mkdirs();
        if (!entry.isDirectory()) {
            BufferedInputStream is = new BufferedInputStream(zip
                    .getInputStream(entry));
            int currentByte;
            // establish buffer for writing file
            byte data[] = new byte[BUFFER];

            // write the current file to disk
            FileOutputStream fos = new FileOutputStream(destFile);
            BufferedOutputStream dest = new BufferedOutputStream(fos,
                    BUFFER);

            // read and write until last byte is encountered
            while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                dest.write(data, 0, currentByte);
            }
            dest.flush();
            dest.close();
            is.close();
        }
        if (currentEntry.endsWith(".zip")) {
            // found a zip file, try to open
            unzip(destFile.getAbsolutePath());
        }
    }
}

public static void main(String[] args) {
    Unzip unzipper=new Unzip();
    try {
        unzipper.unzip("test/test.zip");
    } catch (ZipException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

}

我测试过,它可以工作

【讨论】:

  • 该代码实际上是由 ca.anderson4 编写的;我只是编辑了它(我不喜欢左右滚动)。
  • aaa 好的,我没看到这个:-)。所以我要感谢 ca.anderson4 和你的编辑 ;-)
  • 这只是将所有子文件夹中的所有文件转储到一个文件夹中,从而破坏了整个存档结构
【解决方案4】:

在测试中我注意到 File.mkDirs() 在 Windows 下不起作用...

/** * 对于给定的完整路径名,重新创建所有父目录 **/

    private void createParentHierarchy(String parentName) throws IOException {
        File parent = new File(parentName);
        String[] parentsStrArr = parent.getAbsolutePath().split(File.separator == "/" ? "/" : "\\\\");

        //create the parents of the parent
        for(int i=0; i < parentsStrArr.length; i++){
            StringBuffer currParentPath = new StringBuffer();
            for(int j = 0; j < i; j++){
                currParentPath.append(parentsStrArr[j]+File.separator);
            }
            File currParent = new File(currParentPath.toString());
            if(!currParent.isDirectory()){
                boolean created = currParent.mkdir();
                if(isVerbose)log("creating directory "+currParent.getAbsolutePath());
            }
        }

        //create the parent itself
        if(!parent.isDirectory()){
            boolean success = parent.mkdir();
        }
    }

【讨论】:

    【解决方案5】:

    根据需要进行修改,然后混合一些最佳答案。此版本将:

    • 递归提取 zip 到给定位置

    • 创建空目录

    • 正确关闭拉链


    public static void unZipAll(File source, File destination) throws IOException 
    {
        System.out.println("Unzipping - " + source.getName());
        int BUFFER = 2048;
    
        ZipFile zip = new ZipFile(source);
        try{
            destination.getParentFile().mkdirs();
            Enumeration zipFileEntries = zip.entries();
    
            // Process each entry
            while (zipFileEntries.hasMoreElements())
            {
                // grab a zip file entry
                ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
                String currentEntry = entry.getName();
                File destFile = new File(destination, currentEntry);
                //destFile = new File(newPath, destFile.getName());
                File destinationParent = destFile.getParentFile();
    
                // create the parent directory structure if needed
                destinationParent.mkdirs();
    
                if (!entry.isDirectory())
                {
                    BufferedInputStream is = null;
                    FileOutputStream fos = null;
                    BufferedOutputStream dest = null;
                    try{
                        is = new BufferedInputStream(zip.getInputStream(entry));
                        int currentByte;
                        // establish buffer for writing file
                        byte data[] = new byte[BUFFER];
    
                        // write the current file to disk
                        fos = new FileOutputStream(destFile);
                        dest = new BufferedOutputStream(fos, BUFFER);
    
                        // read and write until last byte is encountered
                        while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                            dest.write(data, 0, currentByte);
                        }
                    } catch (Exception e){
                        System.out.println("unable to extract entry:" + entry.getName());
                        throw e;
                    } finally{
                        if (dest != null){
                            dest.close();
                        }
                        if (fos != null){
                            fos.close();
                        }
                        if (is != null){
                            is.close();
                        }
                    }
                }else{
                    //Create directory
                    destFile.mkdirs();
                }
    
                if (currentEntry.endsWith(".zip"))
                {
                    // found a zip file, try to extract
                    unZipAll(destFile, destinationParent);
                    if(!destFile.delete()){
                        System.out.println("Could not delete zip");
                    }
                }
            }
        } catch(Exception e){
            e.printStackTrace();
            System.out.println("Failed to successfully unzip:" + source.getName());
        } finally {
            zip.close();
        }
        System.out.println("Done Unzipping:" + source.getName());
    }
    

    【讨论】:

      【解决方案6】:

      解压缩后应关闭压缩文件。

      static public void extractFolder(String zipFile) throws ZipException, IOException 
      {
          System.out.println(zipFile);
          int BUFFER = 2048;
          File file = new File(zipFile);
      
          ZipFile zip = new ZipFile(file);
          try
          { 
             ...code from other answers ( ex. NeilMonday )...
          }
          finally
          {
              zip.close();
          }
      }
      

      【讨论】:

        【解决方案7】:

        与 NeilMonday 的回答相同,但提取的是空目录:

        static public void extractFolder(String zipFile) throws ZipException, IOException 
        {
            System.out.println(zipFile);
            int BUFFER = 2048;
            File file = new File(zipFile);
        
            ZipFile zip = new ZipFile(file);
            String newPath = zipFile.substring(0, zipFile.length() - 4);
        
            new File(newPath).mkdir();
            Enumeration zipFileEntries = zip.entries();
        
            // Process each entry
            while (zipFileEntries.hasMoreElements())
            {
                // grab a zip file entry
                ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
                String currentEntry = entry.getName();
                File destFile = new File(newPath, currentEntry);
                //destFile = new File(newPath, destFile.getName());
                File destinationParent = destFile.getParentFile();
        
                // create the parent directory structure if needed
                destinationParent.mkdirs();
        
                if (!entry.isDirectory())
                {
                    BufferedInputStream is = new BufferedInputStream(zip
                    .getInputStream(entry));
                    int currentByte;
                    // establish buffer for writing file
                    byte data[] = new byte[BUFFER];
        
                    // write the current file to disk
                    FileOutputStream fos = new FileOutputStream(destFile);
                    BufferedOutputStream dest = new BufferedOutputStream(fos,
                    BUFFER);
        
                    // read and write until last byte is encountered
                    while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                        dest.write(data, 0, currentByte);
                    }
                    dest.flush();
                    dest.close();
                    is.close();
                }
                else{
                    destFile.mkdirs()
                }
                if (currentEntry.endsWith(".zip"))
                {
                    // found a zip file, try to open
                    extractFolder(destFile.getAbsolutePath());
                }
            }
        }
        

        【讨论】:

          【解决方案8】:

          这是一些代码,我测试它运行良好:

          package com.test;
          
          import java.io.BufferedInputStream;
          import java.io.BufferedOutputStream;
          import java.io.File;
          import java.io.FileOutputStream;
          import java.util.Enumeration;
          import java.util.zip.ZipEntry;
          import java.util.zip.ZipFile;
          
          public class Unzipper {  
              private final static int BUFFER_SIZE = 2048;
              private final static String ZIP_FILE = "/home/anton/test/test.zip";
              private final static String DESTINATION_DIRECTORY = "/home/anton/test/";
              private final static String ZIP_EXTENSION = ".zip";
           
              public static void main(String[] args) {
               System.out.println("Trying to unzip file " + ZIP_FILE); 
                  Unzipper unzip = new Unzipper();  
                  if (unzip.unzipToFile(ZIP_FILE, DESTINATION_DIRECTORY)) {
                   System.out.println("Succefully unzipped to the directory " 
                       + DESTINATION_DIRECTORY);
                  } else {
                   System.out.println("There was some error during extracting archive to the directory " 
                       + DESTINATION_DIRECTORY);
                  }
              } 
          
           public boolean unzipToFile(String srcZipFileName,
             String destDirectoryName) {
            try {
             BufferedInputStream bufIS = null;
             // create the destination directory structure (if needed)
             File destDirectory = new File(destDirectoryName);
             destDirectory.mkdirs();
          
             // open archive for reading
             File file = new File(srcZipFileName);
             ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ);
          
             //for every zip archive entry do
             Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
             while (zipFileEntries.hasMoreElements()) {
              ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
              System.out.println("\tExtracting entry: " + entry);
          
              //create destination file
              File destFile = new File(destDirectory, entry.getName());
          
              //create parent directories if needed
              File parentDestFile = destFile.getParentFile();    
              parentDestFile.mkdirs();    
              
              if (!entry.isDirectory()) {
               bufIS = new BufferedInputStream(
                 zipFile.getInputStream(entry));
               int currentByte;
          
               // buffer for writing file
               byte data[] = new byte[BUFFER_SIZE];
          
               // write the current file to disk
               FileOutputStream fOS = new FileOutputStream(destFile);
               BufferedOutputStream bufOS = new BufferedOutputStream(fOS, BUFFER_SIZE);
          
               while ((currentByte = bufIS.read(data, 0, BUFFER_SIZE)) != -1) {
                bufOS.write(data, 0, currentByte);
               }
          
               // close BufferedOutputStream
               bufOS.flush();
               bufOS.close();
          
               // recursively unzip files
               if (entry.getName().toLowerCase().endsWith(ZIP_EXTENSION)) {
                String zipFilePath = destDirectory.getPath() + File.separatorChar + entry.getName();
          
                unzipToFile(zipFilePath, zipFilePath.substring(0, 
                        zipFilePath.length() - ZIP_EXTENSION.length()));
               }
              }
             }
             bufIS.close();
             return true;
            } catch (Exception e) {
             e.printStackTrace();
             return false;
            }
           } 
          }  
          

          我在这里尝试了投票最高的答案,它不会递归解压缩文件,它只是解压缩第一级的文件。

          来源:Solution which extracts files into a given directory

          另外,请同一个人检查此解决方案:Solution which extracts file in memory

          【讨论】:

            【解决方案9】:
            File dir = new File("BASE DIRECTORY PATH");
            FileFilter ff = new FileFilter() {
            
                @Override
                public boolean accept(File f) {
                    //only want zip files
                    return (f.isFile() && f.getName().toLowerCase().endsWith(".zip"));
                }
            };
            
            File[] list = null;
            while ((list = dir.listFiles(ff)).length > 0) {
                File file1 = list[0];
                //TODO unzip the file to the base directory
            }
            

            【讨论】:

              猜你喜欢
              • 2011-10-30
              • 1970-01-01
              • 2011-05-30
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-12-27
              • 1970-01-01
              • 2022-07-27
              相关资源
              最近更新 更多