Wie kann man den Inhalt eines Verzeichnisses so in einem Zip-Archiv speichern, dass die ursprünglichen Benennungen und die Struktur des Dateibaums erhalten bleiben?
Im Konstruktor der Beispielklasse werden zunächst der Pfad
des zu packenden Verzeichnisses und derjenige des Zielarchivs
deklariert. Um zu einem späteren Zeitpunkt die Pfade der
gepackten Dateien zu ermitteln, wird der Pfad der Zip-Datei
zunächst ohne Dateiendung angegeben.
Die darauf
folgenden Operationen sind in einem try-catch
-Block gekapselt, um Fehler beim Anlegen des Zip-Archivs
abzufangen. Dies geschieht durch einen ZipOutputStream
. Er erzeugt das noch leere Zip-Archiv, indem er einen FileOutputStream
mit dem gewünschten Dateinamen übergeben bekommt.
Der
dann folgende Methodenaufruf erfolgt mir vier Parametern:
- dem
String
des endungslosen Pfades der Zip-Datei - dem Pfad des zu packenden Verzeichnisses
- dem
File
-Objekt dieses Verzeichnisses - dem
ZipOutputStream
Die auf den ersten Blick überflüssig erscheinende
Ähnlichkeit des zweiten und dritten Parameters hat seinen
Sinn darin, dass die Methode rekursiv mit unterschiedlichen File
-Objekten aufgerufen werden muss, der Pfad des
Quellverzeichnisses jedoch konstant gehalten werden muss, um die
relativen Pfade innerhalb des Archivs bestimmen zu können.
Der Methodenaufruf wird nur noch gefolgt vom Schließen des
ZipOutputStreams
.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.swing.SwingUtilities;
public class VerzeichnisZippen {
public VerzeichnisZippen() {
String dirToZip = "/Users/Willi/Desktop/test";
String zipName = "/Users/Willi/Desktop/abrakadabra";
ZipOutputStream zos = null;
try {
File f = new File(zipName + ".zip");
System.out.println("Erzeuge Archiv " + f.getCanonicalPath());
zos = new ZipOutputStream(new FileOutputStream(
f.getCanonicalPath()));
zipDir(zipName, dirToZip, new File(dirToZip), zos);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (zos != null) zos.close();
} catch (IOException ioe) {}
}
}
private void zipDir(String zipName, String dirToZip, File dirToZipFile,
ZipOutputStream zos) {
if (zipName == null || dirToZip == null || dirToZipFile == null
|| zos == null || !dirToZipFile.isDirectory())
return;
FileInputStream fis = null;
try {
File[] fileArr = dirToZipFile.listFiles();
String path;
for (File f : fileArr) {
if (f.isDirectory()) {
zipDir(zipName, dirToZip, f, zos);
continue;
}
fis = new FileInputStream(f);
path = f.getCanonicalPath();
String name = path.substring(dirToZip.length(), path.length());
System.out.println("Packe " + name);
zos.putNextEntry(new ZipEntry(name));
int len;
byte[] buffer = new byte[2048];
while ((len = fis.read(buffer, 0, buffer.length)) > 0) {
zos.write(buffer, 0, len);
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fis != null) fis.close();
} catch (IOException ioe) {}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new VerzeichnisZippen());
}
}
Das eigentliche Packen geschieht in der Methode zipDir()
, die die o.a. Parameter übergeben bekommt. Nach einer
Sicherheitsabfrage, die die Verwendbarkeit der Parameter
sicherstellen soll, wird wiederum ein try-catch
-Block gebildet, um Fehler beim Zippen abzufangen.
In diesem
wird das übergebene Verzeichnis ausgelesen und alle
enthaltenen Dateien und Verzeichnisse als File
-Objekte in einem Array
gespeichert. In einer for-Schleife wird das Array durchlaufen
und jedes File
-Objekt zunächst darauf hin
geprüft, ob es sich um ein Verzeichnis handelt. Ist dies
der Fall, so wird die Methode rekursiv mit diesem Verzeichnis
als drittem Parameter aufgerufen, sodass eine Verarbeitung
dessen Inhalts sichergestellt wird.
Ist dies nicht der Fall,
handelt es sich also um eine Datei, so wird ein FileInputStream
erzeugt, der später zum Auslesen des Dateiinhaltes dient.
Zunächst muss jedoch der ursprünglich Pfad der Datei
relativ zum Quellverzeichnis ermittelt und dieser dann innerhalb
des Zip-Archivs erzeugt werden. Hierzu dient der zweite
Parameter. Vom absoluten Pfad der zu packenden Datei wird der
erste Teil, derjenige also, der den Pfad zum Quellverzeichnis
ausmacht, entfernt. Es verbleibt der angesprochene relative
Pfad.
Die Methode putNextEntry()
des ZipOutputStreams
nimmt ein ZipEntry
-Objekt entgegen. Dies
erhält den ermittelten relativen Pfad als Parameter. Die
Methode erzeugt einen neuen Eintrag in der Archivdatei, der
zunächst jedoch noch leer bleibt.
Das Übertragen der Daten erfolgt über einen
Array-Puffer. Der FileInputStream
liest mit seiner
Methode read()
die zu speichernde Datei aus. Dabei
wird der Inhalt so lange in einem Byte-Array
zwischengespeichert, wie weiterer Inhalt vorhanden ist. Die
Größe der ausgelesenen 'Portionen' wird hierbei durch
die Länge des Arrays bestimmt. Ist das Array gefüllt
wird es an den ZipOutputStream
übergeben,
dessen Methode write()
für eine Speicherung im
Archiv sorgt. Der Inhalt des Arrays wird dann wieder
überschrieben, wieder ausgelesen, etc. bis der Inhalt der
Datei vollständig erfasst wurde.
Nach Schließen
des FileInputStream
beginnet der Zyklus von vorne.
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.