Renaming a File/Folder inside a Zip File in Java?

后端 未结 4 908
小鲜肉
小鲜肉 2020-12-19 17:49

I have a zip file containing a folder structure like

  • main-folder/
    • subFolder1/
    • subFolder2/
    • subFolder3/
      • file3.1
      • fi
相关标签:
4条回答
  • 2020-12-19 18:04

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

    0 讨论(0)
  • 2020-12-19 18:08

    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.

    0 讨论(0)
  • 2020-12-19 18:08

    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.

    0 讨论(0)
  • 2020-12-19 18:26

    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();
        }
    }
    
    0 讨论(0)
提交回复
热议问题