java.util.zip - 重新创建目录结构

2022-09-01 01:17:30

在尝试使用压缩存档时,我遇到了很多问题,其中大部分我都解决了。现在我终于得到了一些输出,我很难得到“正确”的输出。我有一个提取的ODT文件(目录更适合描述),我对它做了一些修改。现在我想压缩该目录以重新创建ODT文件结构。压缩目录并将其重命名为以.odt结尾工作正常,因此应该没有问题。java.util.zip

主要问题是我失去了目录的内部结构。一切都变得“扁平”,我似乎没有找到一种方法来保留原始的多层结构。我将不胜感激,因为我似乎找不到问题。

以下是相关的代码片段:

ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
    FILEPATH.substring(0, FILEPATH.lastIndexOf(SEPARATOR) + 1).concat("test.zip")));
    compressDirectory(TEMPARCH, out);

是系统文件分隔符,是原始ODT的文件路径,我将覆盖它,但出于测试目的,这里没有这样做。我只是在同一目录中写入一个测试.zip文件。SEPARATORFILEPATH

private void compressDirectory(String directory, ZipOutputStream out) throws IOException
{
    File fileToCompress = new File(directory);
    // list contents.
    String[] contents = fileToCompress.list();
    // iterate through directory and compress files.
    for(int i = 0; i < contents.length; i++)
    {
        File f = new File(directory, contents[i]);
        // testing type. directories and files have to be treated separately.
        if(f.isDirectory())
        {
            // add empty directory
            out.putNextEntry(new ZipEntry(f.getName() + SEPARATOR));
            // initiate recursive call
            compressDirectory(f.getPath(), out);
            // continue the iteration
            continue;
        }else{
             // prepare stream to read file.
             FileInputStream in = new FileInputStream(f);
             // create ZipEntry and add to outputting stream.
             out.putNextEntry(new ZipEntry(f.getName()));
             // write the data.
             int len;
             while((len = in.read(data)) > 0)
             {
                 out.write(data, 0, len);
             }
             out.flush();
             out.closeEntry();
             in.close();
         }
     }
 }

包含要 zip 的文件的目录位于用户空间中的某个位置,而不是与生成的文件位于同一目录中。我想这可能是麻烦,但我真的看不出如何。另外,我认为问题可能在于使用相同的流进行输出,但我再次看不到如何。我在一些示例和教程中看到他们使用而不是更改,这给了我一个空的zip文件。getPath()getName()


答案 1

URI 类对于处理相对路径非常有用。

File mydir = new File("C:\\mydir");
File myfile = new File("C:\\mydir\\path\\myfile.txt");
System.out.println(mydir.toURI().relativize(myfile.toURI()).getPath());

上面的代码将发出字符串 。path/myfile.txt

为完整起见,以下是存档目录的方法:zip

  public static void zip(File directory, File zipfile) throws IOException {
    URI base = directory.toURI();
    Deque<File> queue = new LinkedList<File>();
    queue.push(directory);
    OutputStream out = new FileOutputStream(zipfile);
    Closeable res = out;
    try {
      ZipOutputStream zout = new ZipOutputStream(out);
      res = zout;
      while (!queue.isEmpty()) {
        directory = queue.pop();
        for (File kid : directory.listFiles()) {
          String name = base.relativize(kid.toURI()).getPath();
          if (kid.isDirectory()) {
            queue.push(kid);
            name = name.endsWith("/") ? name : name + "/";
            zout.putNextEntry(new ZipEntry(name));
          } else {
            zout.putNextEntry(new ZipEntry(name));
            copy(kid, zout);
            zout.closeEntry();
          }
        }
      }
    } finally {
      res.close();
    }
  }

这个代码不会保留日期,我不确定它会如何对符号链接之类的东西做出反应。不会尝试添加目录条目,因此不会包含空目录。

相应的命令:unzip

  public static void unzip(File zipfile, File directory) throws IOException {
    ZipFile zfile = new ZipFile(zipfile);
    Enumeration<? extends ZipEntry> entries = zfile.entries();
    while (entries.hasMoreElements()) {
      ZipEntry entry = entries.nextElement();
      File file = new File(directory, entry.getName());
      if (entry.isDirectory()) {
        file.mkdirs();
      } else {
        file.getParentFile().mkdirs();
        InputStream in = zfile.getInputStream(entry);
        try {
          copy(in, file);
        } finally {
          in.close();
        }
      }
    }
  }

它们所依赖的实用方法:

  private static void copy(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    while (true) {
      int readCount = in.read(buffer);
      if (readCount < 0) {
        break;
      }
      out.write(buffer, 0, readCount);
    }
  }

  private static void copy(File file, OutputStream out) throws IOException {
    InputStream in = new FileInputStream(file);
    try {
      copy(in, out);
    } finally {
      in.close();
    }
  }

  private static void copy(InputStream in, File file) throws IOException {
    OutputStream out = new FileOutputStream(file);
    try {
      copy(in, out);
    } finally {
      out.close();
    }
  }

缓冲区大小完全是任意的。


答案 2

我看到你的代码中有2个问题,

  1. 您不保存目录路径,因此无法将其取回。
  2. 在 Windows 上,您需要使用“/”作为路径分隔符。某些解压缩程序不喜欢 \。

我包括我自己的版本供您参考。我们使用这个来压缩照片下载,以便它与各种解压缩程序一起使用。它保留目录结构和时间戳。

  public static void createZipFile(File srcDir, OutputStream out,
   boolean verbose) throws IOException {

  List<String> fileList = listDirectory(srcDir);
  ZipOutputStream zout = new ZipOutputStream(out);

  zout.setLevel(9);
  zout.setComment("Zipper v1.2");

  for (String fileName : fileList) {
   File file = new File(srcDir.getParent(), fileName);
   if (verbose)
    System.out.println("  adding: " + fileName);

   // Zip always use / as separator
   String zipName = fileName;
   if (File.separatorChar != '/')
    zipName = fileName.replace(File.separatorChar, '/');
   ZipEntry ze;
   if (file.isFile()) {
    ze = new ZipEntry(zipName);
    ze.setTime(file.lastModified());
    zout.putNextEntry(ze);
    FileInputStream fin = new FileInputStream(file);
    byte[] buffer = new byte[4096];
    for (int n; (n = fin.read(buffer)) > 0;)
     zout.write(buffer, 0, n);
    fin.close();
   } else {
    ze = new ZipEntry(zipName + '/');
    ze.setTime(file.lastModified());
    zout.putNextEntry(ze);
   }
  }
  zout.close();
 }

 public static List<String> listDirectory(File directory)
   throws IOException {

  Stack<String> stack = new Stack<String>();
  List<String> list = new ArrayList<String>();

  // If it's a file, just return itself
  if (directory.isFile()) {
   if (directory.canRead())
    list.add(directory.getName());
   return list;
  }

  // Traverse the directory in width-first manner, no-recursively
  String root = directory.getParent();
  stack.push(directory.getName());
  while (!stack.empty()) {
   String current = (String) stack.pop();
   File curDir = new File(root, current);
   String[] fileList = curDir.list();
   if (fileList != null) {
    for (String entry : fileList) {
     File f = new File(curDir, entry);
     if (f.isFile()) {
      if (f.canRead()) {
       list.add(current + File.separator + entry);
      } else {
       System.err.println("File " + f.getPath()
         + " is unreadable");
       throw new IOException("Can't read file: "
         + f.getPath());
      }
     } else if (f.isDirectory()) {
      list.add(current + File.separator + entry);
      stack.push(current + File.separator + f.getName());
     } else {
      throw new IOException("Unknown entry: " + f.getPath());
     }
    }
   }
  }
  return list;
 }
}

推荐