2013-04-09 16 views
5

Chcę utworzyć archiwum zip w Javie, gdzie każdy z zawartych plików jest tworzony przez szeregowanie niektórych obiektów. Mam problem z prawidłowym zamknięciem strumieni.Pisz z ObjectOutputStream do wielu ZipEntrys w jednym ZipOutputStream

Kod wygląda następująco:

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 
} 

Wiem oczywiście, że należy zamknąć każdy strumień po ukończeniu go używać, więc powinienem zamknąć ObjectOutputStream s we wskazanych pozycjach. Jednak zamknięcie ObjectOutputStream s również zamknie ZipOutputStream, którego wciąż potrzebuję.

Nie chcę pominąć połączenia z numerem ObjectOutputStream.close(), ponieważ nie chcę polegać na tym, że obecnie nie ma ono więcej niż flush() i reset().

Nie mogę również użyć pojedynczej instancji ObjectOutputStream, ponieważ wtedy brakuje mi nagłówka strumienia zapisanego przez konstruktora (każdy pojedynczy plik w archiwum zip nie byłby serializacją całego obiektu i nie mogłem odserializować ich niezależnie).

Ten sam problem występuje podczas ponownego odczytu pliku.

Jedynym sposobem, widzę byłoby zawinąć ZipOutputStream w pewnego rodzaju „CloseProtectionOutputStream”, który przekazuje wszystkie sposoby z wyjątkiem close() przed podaniem go do ObjectOutputStream. Jednak wydaje się to dość hacky i zastanawiam się, czy przegapiłem lepsze rozwiązanie w API.

+1

IMHO problem leży w ZIP API, a nie w OOS. Zamiast zabawnych rzeczy, takich jak 'closeEntry', powinno ci dać odpowiedni strumień wyjściowy, który możesz" zamknąć "(zobacz TrueZip). – maaartinus

Odpowiedz

2

Jeśli opakowanie OutputStream zgłasza wyjątek po zamknięciu więcej niż raz, nie jest to hackowanie. Możesz utworzyć opakowanie dla każdego wpisu zip.

Z architektonicznego punktu widzenia, autor ObjectOutputStream powinien mieć możliwość wyłączenia kaskadowania close(). Po prostu próbujesz obejść jego brak API.

2

W tym przypadku i ze wszystkich powodów, o których wspomniałeś, po prostu nie wypaliłbym mojego ObjectOutputStream do ZipOutputStream. Zamiast tego przekształcić do postaci byte[], a następnie zapisać go bezpośrednio w ZipOutputStream. W ten sposób możesz zamknąć ObjectOutputStream, a każdy wyprodukowany byte[] będzie miał odpowiedni nagłówek z serializera. Jedną wadą jest to, że skończyłeś z byte[] w pamięci, której wcześniej nie miałeś, ale jeśli pozbędziesz się go od razu, zakładając, że nie mówimy o milionach obiektów, śmieciarz nie powinien mieć kłopotów sprzątanie.

Tylko moje dwa centy ...

To przynajmniej brzmi mniej hacky niż podklasy strumienia, który zmienia zachowanie close().

+0

Dzięki za pomysł, wydaje się, że to odpowiednie rozwiązanie. W moim przypadku użycia mam setki megabajtów lub nawet gigabajtów danych, więc niestety nie jest to wykonalne. –

2

Jeśli masz zamiar rzucić ObjectOutputStream dala tak, to powinno być wystarczające, aby wywołać flush() zamiast close(), ale jak mówisz w pytaniu najbezpieczniejszym rozwiązaniem jest prawdopodobnie używać owijkę wokół leżącej ZipOutputStream który blokuje wywołanie close(). Apache commons-io ma w tym celu CloseShieldOutputStream.

Powiązane problemy