2013-04-09 8 views
5

Ich möchte ein Zip-Archiv in Java erstellen, wo jede enthaltene Datei durch Serialisierung einiger Objekte erzeugt wird. Ich habe ein Problem beim korrekten Schließen der Streams.Schreiben mit ObjectOutputStream in mehrere ZipEntrys in einem einzigen ZipOutputStream

Der Code sieht wie folgt aus:

try (OutputStream os = new FileOutputStream(file); 
    ZipOutputStream zos = new ZipOutputStream(os);) { 

    ZipEntry ze; 
    ObjectOutputStream oos; 

    ze = new ZipEntry("file1"); 
    zos.putNextEntry(ze); // start first file in zip archive 
    oos = new ObjectOutputStream(zos); 
    oos.writeObject(obj1a); 
    oos.writeObject(obj1b); 
    // I want to close oos here without closing zos 
    zos.closeEntry(); // end first file in zip archive 

    ze = new ZipEntry("file2"); 
    zos.putNextEntry(ze); // start second file in zip archive 
    oos = new ObjectOutputStream(zos); 
    oos.writeObject(obj2a); 
    oos.writeObject(obj2b); 
    // And here again 
    zos.closeEntry(); // end second file in zip archive 
} 

Ich weiß natürlich, dass ich jeden Strom schließen sollte, nachdem es mit Finishing, also sollte ich die ObjectOutputStream s in den angegebenen Positionen schließen. Schließen der ObjectOutputStream s würde aber auch die ZipOutputStream schließen, die ich noch brauche.

Ich möchte nicht den Anruf an ObjectOutputStream.close() weglassen, weil ich nicht auf die Tatsache verlassen möchte, dass es derzeit nicht mehr als flush() und reset().

Ich kann auch keine einzige -Instanz verwenden, weil ich dann den Stream-Header vermisse, der vom Konstruktor geschrieben wird (jede einzelne Datei im ZIP-Archiv wäre keine vollständige Objektserialisierungsdatei und ich konnte sie nicht deserialisieren unabhängig).

Das gleiche Problem tritt beim erneuten Lesen der Datei auf.

Der einzige Weg, ich sehe, würde die ZipOutputStream in einer Art „CloseProtectionOutputStream“ wickeln, die alle Methoden außer close() weiterleiten, bevor ihm die ObjectOutputStream geben. Dies scheint jedoch eher hacky und ich frage mich, ob ich eine bessere Lösung in der API verpasst habe.

+1

IMHO das Problem liegt in der ZIP API und nicht in OOS. Anstelle von lustigen Dingen wie 'closeEntry' sollte es Ihnen einen richtigen Ausgabestrom geben, den Sie' schließen' könnten (siehe TrueZip). – maaartinus

Antwort

2

Wenn Ihr OutputStream Wrapper beim Schließen eine Ausnahme auslöst, ist es kein Hack. Sie können für jeden Zip-Eintrag einen Wrapper erstellen.

Aus architektonischer Sicht sollte der Autor eine Option zur Verfügung gestellt haben, close() Kaskadierung zu deaktivieren. Sie sind nur Workaround seiner fehlenden API.

2

In diesem Fall, und aus all den Gründen, die Sie erwähnten, würde ich einfach nicht meine ObjectOutputStream an die ZipOutputStream. Stattdessen serialisieren Sie eine byte[] und schreiben Sie dann direkt in die ZipOutputStream. Auf diese Weise können Sie die ObjectOutputStream schließen und jede byte[], die Sie produzieren, wird den richtigen Header vom Serializer haben. Ein Nachteil ist, dass Sie mit einer byte[] in Erinnerung sind, die Sie vorher nicht hatten, aber wenn Sie es sofort loswerden, vorausgesetzt, wir sprechen nicht über Millionen von Objekten, sollte der Müllsammler keine harte Zeit haben Aufräumen.

Just my two cents ...

Es zumindest klingt weniger hacky als eine Strom-Unterklasse, die das close() Verhalten ändert.

+0

Dank für die Idee, im Allgemeinen scheint es wie eine geeignete Lösung. In meinem Anwendungsfall habe ich Hunderte von Megabytes oder sogar Gigabytes an Daten, das ist leider nicht machbar. –

2

Wenn Sie beabsichtigen, die Object weg sowieso zu werfen, dann sollte es ausreichend sein, flush() zu nennen, anstatt close(), aber wie Sie in der Frage sagen, dass die sicherste Methode ist wahrscheinlich einen Wrapper um den zugrunde liegenden ZipOutputStream zu verwenden, dass die Blöcke der Anruf close(). Apache commons-io hat CloseShieldOutputStream für diesen Zweck.

Verwandte Themen