Java 程序忽略 zip 文件中的所有文件

Java program ignoring all the files inside the zip file

当我通过控制台提供 zip 文件夹路径时,我有程序。它将遍历该文件夹中的每个项目(每个子项目、子项的子项等)。但是如果它遇到一个 zip 文件夹,它将忽略 zip 文件夹中的所有内容,我需要阅读所有内容,包括 zip 文件夹中的文件。

这是遍历每个项目的方法:

    public static String[] getLogBuffers(String path) throws IOException//path is given via console
  {
    String zipFileName = path;
    String destDirectory = path;
    BufferedInputStream errorLogBuffer = null;
    BufferedInputStream windowLogBuffer = null;
    String strErrorLogFileContents="";
    String strWindowLogFileContents="";
    String[] errorString=new String[2];



    byte[] buffer = new byte[1024];
    ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFileName));
    ZipEntry zipEntry = zis.getNextEntry();
    while (zipEntry != null)
    {
      String filePath = destDirectory + "/" + zipEntry.getName();
      System.out.println("unzipping" + filePath);
      if (!zipEntry.isDirectory())
      {
                if (zipEntry.getName().endsWith("errorlog.txt"))
                {
                  ZipFile zipFile = new ZipFile(path);
                  InputStream errorStream = zipFile.getInputStream(zipEntry);
                  BufferedInputStream bufferedInputStream=new BufferedInputStream(errorStream);
                  byte[] contents = new byte[1024];
                  System.out.println("ERRORLOG NAME"+zipEntry.getName());
                  int bytesRead = 0;
                  while((bytesRead = bufferedInputStream.read(contents)) != -1) {
                    strErrorLogFileContents += new String(contents, 0, bytesRead);
                  }

                }
                if (zipEntry.getName().endsWith("windowlog.txt"))
                { ZipFile zipFile = new ZipFile(path);
                  InputStream windowStream = zipFile.getInputStream(zipEntry);
                  BufferedInputStream bufferedInputStream=new BufferedInputStream(windowStream);
                  byte[] contents = new byte[1024];
                  System.out.println("WINDOWLOG NAME"+zipEntry.getName());

                  int bytesRead = 0;
                  while((bytesRead = bufferedInputStream.read(contents)) != -1) {
                    strWindowLogFileContents += new String(contents, 0, bytesRead);
                  }

                }

      }
    
      zis.closeEntry();
      zipEntry = zis.getNextEntry();

    }
    errorString[0]=strErrorLogFileContents;
    errorString[1]=strWindowLogFileContents;
    zis.closeEntry();
    zis.close();
    System.out.println("Buffers ready");
    return errorString;
  }

在父 zip 文件夹中访问的项目(我的控制台输出):

unzippingC:logFolders/logX3.zip/logX3/
unzippingC:logFolders/logX3.zip/logX3/Anan/
unzippingC:logFolders/logX3.zip/logX3/Anan/errorreports/
unzippingC:logFolders/logX3.zip/logX3/Anan/errorreports/2021-11-23_103518.zip
unzippingC:logFolders/logX3.zip/logX3/Anan/errorreports/errorlog.txt
unzippingC:logX3.zip/logX3/Anan/errorreports/version.txt
unzippingC:logFolders/logX3.zip/logX3/Anan/errorreports/windowlog.txt

如您所见,该程序只运行到 2021-11-23_103518.zip,之后进入另一条路径,但 2021-11-23_103518.zip 有我需要的子项目(文件)使用权 感谢任何帮助,谢谢

zip 文件不是文件夹。 尽管 Windows 将 zip 文件视为文件夹,*它不是一个文件夹。 .zip 文件是一个包含内部 table 条目的单个文件,每个条目都包含压缩数据。

您阅读的每个内部 .zip 文件都需要一个新的 ZipFile or ZipInputStream。没有办法解决这个问题。

您不应创建新的 ZipFile 实例来读取同一 .zip 文件的条目。您只需要一个 ZipFile 对象。您可以使用其 entries() method, and you can read each entry with the ZipFile’s getInputStream 方法浏览其条目。

(如果使用多个对象读取同一个 zip 文件会 运行 进入 Windows 上的文件锁定问题,我不会感到惊讶。)

try (ZipFile zipFile = new ZipFile(path))
{
    Enumeration<? extends ZipEntry> entries = zipFile.entries();
    while (entries.hasMoreElements())
    {
        ZipEntry zipEntry = entries.nextElement();

        if (zipEntry.getName().endsWith("errorlog.txt"))
        {
            try (InputStream errorStream = zipFile.getInputStream(zipEntry))
            {
                // ...
            }
        }
    }
}

请注意,没有创建其他 ZipFile 或 ZipInputStream 对象。只有 zipFile 读取并遍历文件。还要注意使用 try-with-resources 语句来隐式关闭 ZipFile 和 InputStream。

你不应该使用 += 来构建一个字符串。 这样做会创建很多中间字符串对象,这些对象必须被垃圾收集,这会伤害你的程序的性能。您应该将每个 zip 条目的 InputStream 包装在一个包含组合日志的 InputStreamReader, then use that Reader’s transferTo method to append to a single StringWriter 中。

String strErrorLogFileContents = new StringWriter();
String strWindowLogFileContents = new StringWriter();

try (ZipFile zipFile = new ZipFile(path))
{
    Enumeration<? extends ZipEntry> entries = zipFile.entries();
    while (entries.hasMoreElements())
    {
        ZipEntry zipEntry = entries.nextElement();

        if (zipEntry.getName().endsWith("errorlog.txt"))
        {
            try (Reader entryReader = new InputStreamReader(
                zipFile.getInputStream(zipEntry),
                StandardCharsets.UTF_8))
            {
                entryReader.transferTo(strErrorLogFileContents);
            }
        }
    }
}

注意 StandardCharsets 的使用。UTF_8. 在不指定字符集的情况下从字节创建字符串几乎是不正确的。如果您不提供字符集,Java 将使用系统的默认字符集,这意味着您的程序在 Windows 中的行为将与在其他操作系统上的行为不同。

如果你被Java 8卡住了,你将没有Reader的transferTo方法,所以你必须自己完成工作:

        if (zipEntry.getName().endsWith("errorlog.txt"))
        {
            try (Reader entryReader = new BufferedReader(
                new InputStreamReader(
                    zipFile.getInputStream(zipEntry),
                    StandardCharsets.UTF_8)))
            {
                int c;
                while ((c = entryReader.read()) >= 0)
                {
                    strErrorLogFileContents.write(c);
                }
            }
        }

使用 BufferedReader 意味着您无需创建自己的数组并自行实现批量读取。 BufferedReader 已经为您做到了。

如上所述,本身是内部 zip 文件的 zip 条目需要全新的 ZipFile 或 ZipInputStream 对象才能读取它。我推荐 copying the entry to a temporary file,因为众所周知从另一个 ZipInputStream 生成的 ZipInputStream 读取速度很慢,然后在读取完成后删除临时文件。

try (ZipFile zipFile = new ZipFile(path))
{
    Enumeration<? extends ZipEntry> entries = zipFile.entries();
    while (entries.hasMoreElements())
    {
        ZipEntry zipEntry = entries.nextElement();

        if (zipEntry.getName().endsWith(".zip"))
        {
            Path tempZipFile = Files.createTempFile(null, ".zip");
            try (InputStream errorStream = zipFile.getInputStream(zipEntry))
            {
                Files.copy(errorStream, tempZipFile,
                    StandardCopyOption.REPLACE_EXISTING);
            }

            String[] logsFromZip = getLogBuffers(tempZipFile.toString());

            strErrorLogFileContents.write(logsFromZip[0]);
            strWindowLogFileContents.write(logsFromZip[1]);

            Files.delete(tempZipFile);
        }
    }
}

最后,考虑为您的 return 值创建一个有意义的 class。 字符串数组很难理解。调用者不知道它总是恰好包含两个元素,也不知道这两个元素是什么。自定义 return 类型会很短:

public class Logs {
    private final String errorLog;

    private final String windowLog;

    public Logs(String errorLog,
                String windowLog)
    {
        this.errorLog = errorLog;
        this.windowLog = windowLog;
    }

    public String getErrorLog()
    {
        return errorLog;
    }

    public String getWindowLog()
    {
        return windowLog;
    }
}

从 Java 16 开始,您可以使用 record 使声明更短:

public record Logs(String errorLog,
                   String windowLog)
{ }

无论是使用记录还是写出 class,都可以在方法中将其用作 return 类型:

public static Logs getLogBuffers(String path) throws IOException
{
    // ...

    return new Logs(
        strErrorLogFileContents.toString(),
        strWindowLogFileContents.toString());
}

* Windows 资源管理器 shell 将 zip 文件视为文件夹的做法是一个非常糟糕的用户界面。我知道我不是唯一这么想的人。它通常最终会使用户的事情变得更困难而不是更容易。