4

我有一个包含文件夹结构的 zip 文件,例如

  • 主文件夹/
    • 子文件夹1/
    • 子文件夹2/
    • 子文件夹3/
      • 文件3.1
      • 文件3.2

我想重命名文件夹main-folder,让我们说versionXY在那个使用 Java 的 zip 文件中。

有没有比提取整个 zip 文件并使用新文件夹名称重新创建一个更简单的方法?

4

4 回答 4

4

Zip 是一种存档格式,因此变异通常涉及重写文件。

zip 的一些特殊功能也妨碍了(zip 充满了“功能”)。除了存档末尾的中央目录外,每个组件文件前面都有其文件名。Zip 没有目录的概念——文件名只是恰好包含"/"字符的字符串(以及诸如"../".

因此,您确实需要使用ZipInputStreamand复制文件,并随时ZipOutputStream重命名。如果你真的想要,你可以重写文件,做你自己的缓冲。该过程确实会导致内容被重新压缩,因为标准 API 无法获取压缩形式的数据。

编辑: @Doval 指出@megasega 的答案在 NIO 中使用Zip 文件系统提供程序,在 Java SE 7 中是新的(相对于这个答案)。它的性能可能不会很好,就像 RISC OS 的 GUI 中的存档文件系统一样三十年前。

于 2009-05-11T13:15:45.160 回答
3

我认为您将能够使用Commons Compress找到此任务的帮助,尤其是ZipArchiveEntry

于 2009-05-11T12:37:37.200 回答
3

我知道你问过 Java,但只是出于存档目的,我想我会写一篇关于 .NET 的说明。

DotNetZip是一个用于 zip 文件的 .NET 库,允许重命名条目。正如 Tom Hawtin 的回复所述,目录不是 zip 文件元数据中的一流实体,因此,我所知道的任何 zip 库都没有公开“重命名目录”动词。但是有些库允许您重命名所有具有指示特定目录的名称的条目,从而为您提供所需的结果。

在 DotNetZip 中,它看起来像这样:

 var regex = new Regex("/OldDirName/.*$");
 int renameCount= 0;
 using (ZipFile zip = ZipFile.Read(ExistingZipFile))
 {
    foreach (ZipEntry e in zip)
    {
        if (regex.IsMatch(e.FileName))
        {
            // rename here
            e.FileName = e.FileName.Replace("/OldDirName/", "/NewDirName/");
            renameCount++;
        }
    }
    if (renameCount > 0)
    {
        zip.Comment = String.Format("This archive has been modified. {0} entries have been renamed.", renameCount);
        // any changes to the entries are made permanent by Save()
        zip.Save();  // could also save to a new zip file here
    }
 }

您还可以在 using 子句中添加或删除条目。

如果您保存到同一个文件,则 DotNetZip 仅重写更改的元数据 - 条目标题和重命名条目的中央目录记录,这可以节省大型存档的时间。如果您保存到新文件或流中,则会写入所有 zip 数据。

于 2009-05-11T14:56:23.713 回答
3

这是在耍花招。速度极快,因为它仅适用于中央目录而不适用于文件。

//  rezip( zipfile, "/main-folder", "/versionXY" );

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;


protected void rezip( String zipfile, String olddir, String newdir ) {

    Path zipFilePath = Paths.get( zipfile );
    try (FileSystem fs = FileSystems.newFileSystem( zipFilePath, null )) {
        Path oldpathInsideZipPath = fs.getPath( olddir );
        if( ! Files.exists( Paths.get( newdir ) ) )
            Files.createDirectory( Paths.get( newdir ) );

        if ( Files.exists( oldpathInsideZipPath, LinkOption.NOFOLLOW_LINKS ) ) {
            Files.walkFileTree(oldpathInsideZipPath, new SimpleFileVisitor<Path>() {
                 @Override
                 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
                     throws IOException
                 {
                     if( file.toString().indexOf( olddir ) > -1 ){
                         String a = file.toString().replaceAll( olddir, newdir );
                         Path b = fs.getPath( a );
                         if( ! Files.exists( b.getParent() ) ){
                             Files.createDirectories( b.getParent() );
                         }
                         Files.move( file, b, LinkOption.NOFOLLOW_LINKS );
                     }
                     return FileVisitResult.CONTINUE;
                 }
                 @Override
                 public FileVisitResult postVisitDirectory(Path dir, IOException e)
                     throws IOException
                 {
                     if (e == null) {
                         Files.delete(dir);
                         return FileVisitResult.CONTINUE;
                     } else {
                         // directory iteration failed
                         throw e;
                     }
                 }
             });
        }
        fs.close();
    } catch ( Exception e ) {
        e.printStackTrace();
    }
}
于 2016-08-19T11:56:41.143 回答