在 Java 中重命名 Zip 文件内的文件/文件夹?

发布于 2024-07-19 06:19:47 字数 324 浏览 3 评论 0 原文

这样的文件夹结构

  • 我有一个 zip 文件,其中包含像main-folder/
    • 子文件夹1/
    • 子文件夹2/
    • 子文件夹3/
      • 文件3.1
      • 文件3.2

我想使用 Java 将文件夹 main-folder 重命名为 versionXY

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

I have a zip file containing a folder structure like

  • main-folder/
    • subFolder1/
    • subFolder2/
    • subFolder3/
      • file3.1
      • file3.2

I would like to rename folder main-folder to let's say versionXY inside that very zip file using Java.

Is there a simpler way than extracting the whole zip file and recreating a new one using the new folder names?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

策马西风 2024-07-26 06:19:47

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

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

因此,您确实需要使用 ZipInputStreamZipOutputStream 来复制文件,如果您确实想要的话,您可以在适当的位置重写该文件,并进行自己的缓冲。确实会导致内容被重新压缩,因为标准 API 无法获取压缩形式的数据

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

Zip is an archive format, so mutating generally involves rewriting the file.

Some particular features of zip also get in the way (zip is full of "features"). As well as the central directory at the end of the archive, each component file is preceded by its file name. Zip doesn't have a concept of directories - file names are just strings that happen to include "/" characters (and substrings such as "../".

So, you really need to copy the file using ZipInputStream and ZipOutputStream, renaming as you go. If you really wanted to you could rewrite the file in place doing your own buffering. The process does cause the contents to be recompressed as the standard API has no means of obtaining the data in compressed form.

Edit: @Doval points out that @megasega's answer uses Zip File System Provider in NIO, new (relative to this answer) in Java SE 7. It's performance will likely be not great, as were the archive file systems in RISC OS' GUI of thirty years ago.

书间行客 2024-07-26 06:19:47

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

I think you'll be able to find help for this task using the Commons Compress, especially ZipArchiveEntry

万劫不复 2024-07-26 06:19:47

我知道您询问了有关 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 数据都会被写入。

I know you asked about Java but just for archival purposes I thought I would contribute a note about .NET.

DotNetZip is a .NET library for zip files that allows renaming of entries. As Tom Hawtin's reply states, directories are not first-class entities in the zip file metadata, and as a result, no zip libraries that I know of expose a "rename directory" verb. But some libraries allow you to rename all the entries that have names that indicate a particular directory, which gives you the result you want.

In DotNetZip, it would look like this:

 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
    }
 }

You can also add or remove entries, inside the using clause.

If you save to the same file, then DotNetZip rewrites only the changed metadata - the entry headers and the central directory records for renamed entries, which saves time with large archives. If you save to a new file or stream, then all of the zip data gets written.

半透明的墙 2024-07-26 06:19:47

这就是诀窍。 速度极快,因为它仅适用于中央目录而不适用于文件。

//  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();
    }
}

This is doing the trick. Blazing fast since it works only on the central directory and not the files.

//  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();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文