Rinominare un file / cartella all'interno di un file zip in Java?
Domanda
Ho un file zip contenente una struttura di cartelle come
- main-cartella /
- SubFolder1 /
- SubFolder2 /
- subFolder3 /
- file3.1
- file3.2
Vorrei rinominare la cartella main-folder
al diciamo versionXY
dentro quel file utilizzando Java zip.
C'è un modo più semplice di estrarre l'intero file zip e ricreare uno nuovo utilizzando i nuovi nomi delle cartelle?
Soluzione
Zip è un formato di archivio, in modo da mutare in genere comporta la riscrittura del file.
Alcuni particolari caratteristiche di zip ottengono anche nel modo (zip è piena di "caratteristiche"). Così come la directory centrale alla fine dell'archivio, ogni file componente è preceduto dal nome del file. Zip non ha un concetto di directory - i nomi dei file sono solo le stringhe che capita di includere "/" personaggi (e stringhe, come" ../".
Quindi, si ha realmente bisogno di copiare il file usando ZipInputStream
e ZipOutputStream
, rinominare, come si va. Se si voleva davvero a voi potrebbe riscrivere il file al suo posto facendo il proprio buffering. Il processo non causare i contenuti da ricompresso come API standard ha alcun mezzo per ottenere i dati in forma compressa.
Altri suggerimenti
So che hai chiesto di Java, ma solo a scopo di archiviazione ho pensato di contribuire con una nota su .NET.
DotNetZip è una libreria .NET per i file zip che consente la ridenominazione di voci. Come afferma risposta di Tom Hawtin, directory non sono entità di prima classe nei metadati del file zip, e, di conseguenza, nessuna libreria zip che so di esporre un "rinominare directory" verbo. Ma alcune librerie permettono di rinominare tutte le voci che hanno i nomi che indicano una particolare directory, che vi dà il risultato desiderato.
In DotNetZip, sarebbe simile a questa:
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
}
}
È inoltre possibile aggiungere o rimuovere elementi, all'interno della clausola di utilizzo.
Se si salva sullo stesso file, quindi DotNetZip riscrive solo il mutato metadati - le intestazioni di ingresso e le annotazioni di directory centrale per le voci rinominati, che consente di risparmiare tempo con grandi archivi. Se si salva un nuovo file o un flusso, allora tutti i dati zip ottiene scritti.
Credo che sarete in grado di trovare aiuto per questo compito utilizzando il Commons Compress , soprattutto ZipArchiveEntry
Questo sta facendo il trucco. Velocissimo dato che funziona solo sulla directory centrale e non i file.
// 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();
}
}