Переименовать файл / папку внутри Zip-файла в Java? - PullRequest
3 голосов
/ 11 мая 2009

У меня есть zip-файл, содержащий структуру папок, например

  • Основная папка /
    • subFolder1 /
    • subFolder2 /
    • subFolder3 /
      • file3.1
      • file3.2

Я бы хотел переименовать папку main-folder, скажем, versionXY внутри этого самого zip-файла, используя Java.

Есть ли более простой способ, чем извлечь весь zip-файл и воссоздать новый, используя новые имена папок?

Ответы [ 4 ]

4 голосов
/ 11 мая 2009

Zip - это формат архива, поэтому изменение обычно включает в себя переписывание файла.

Некоторые особенности zip также мешают (zip полон «функций»). Как и центральный каталог в конце архива, каждому файлу компонента предшествует его имя файла. У Zip нет концепции каталогов - имена файлов - это просто строки, в которых есть символы "/" (и подстроки, такие как "../".

Итак, вам действительно нужно скопировать файл, используя ZipInputStream и ZipOutputStream, переименовывая по мере необходимости. Если вы действительно хотите, вы можете переписать файл на месте, выполнив собственную буферизацию. Этот процесс вызывает повторное сжатие содержимого, поскольку стандартный API не имеет средств для получения данных в сжатом виде.

4 голосов
/ 11 мая 2009

Я знаю, что вы спрашивали о Java, но только для архивных целей я подумала, что внесу заметку о .NET.

DotNetZip - это библиотека .NET для zip-файлов, которая позволяет переименовывать записи. Как говорится в ответе Тома Хоутина, каталоги не являются первоклассными объектами в метаданных 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 будут записаны.

3 голосов
/ 11 мая 2009

Я думаю, вы сможете найти помощь для этой задачи, используя Commons Compress , особенно ZipArchiveEntry

0 голосов
/ 19 августа 2016

Это делает трюк. Слишком быстро, так как он работает только с центральным каталогом, а не с файлами.

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