[java] ZipOutputStream en encodings

Pagina: 1
Acties:

  • Sammy
  • Registratie: Maart 2000
  • Laatst online: 03-05 10:41
Mijn doel is om een servlet een zip-file met txt-bestanden te laten uitpoepen. Dit leek te lukken, tot de zip-file corrupt bleek. Na een hoop gefrot heb ik maar een stand-alone testcase gemaakt, die de essentie van het probleem weergeeft:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ZipTester{
    
    public static void main(String args[]){
        
        try{
            
            ByteArrayOutputStream bout = new ByteArrayOutputStream(); 
            ZipOutputStream zout = new ZipOutputStream(bout);
            
            byte[] b = "test".getBytes(); // **1 
            
            ZipEntry zEntry = new ZipEntry("test.txt");
            zEntry.setSize(b.length);
            zout.putNextEntry(zEntry); 
            zout.write(b,0,b.length); 
            zout.closeEntry(); 
            
            zout.finish();
            
            zout.flush();
        bout.flush();
            
            String zip = bout.toString(); // **2
            
           bout.close();
            
            
            FileOutputStream fout = new FileOutputStream(new File("test.zip"));
            fout.write(zip.getBytes()); // **3
        }
        catch(Exception e){
            
            System.out.println("help! : " + e);
        }

    }
    
}


Het lijkt wat omslachtig om van zip weer de bytes te nemen en dat naar een file te schrijven, maar uiteindelijk wordt zip dus gewoon in de output van de HttpResponse geschreven. Dit is dus een faire 'simulatie'. Deze code werkt gewoon op mijn WinXP bak (java 1.4.2_03_b02). De zip die echter op de productieserver (RedHat, java 1.4.2_04-b05) wordt gegenereerd met deze code, is corrupt:

code:
1
2
3
4
5
6
WinZip test.zip output:

Use Path: no   Overlay Files: yes
Extracting test.txt
bad CRC d87f7e0c  (should be 3f7f7e0c)
Warning: the size of the extracted file (4) does not match the uncompressed size (0) recorded in the zip file


Overigens ben ik ook al andere foutmelding over een corrupte central directory tegengekomen. Detail: als ik de zip extract op mijn Mac, is er geen vuiltje aan de lucht (volgens mij ignored die alle CRC fouten?!), maar het is duidelijk geen valid zip bestand.

Nu is het zo dat ik 3 plekken gemarkeerd heb in de sourcecode van mijn test. Al deze methoden zijn ook overloaded met een argument voor de encoding. Als die niet opgegeven wordt, kiest java de def. encoding van het platform (volgens API). Nu dacht ik mijn probleem op te lossen door expliciet een encoding op te geven bij 1, 2 en 3. Als ik dat doe, levert me dat op zowel WinXP en RedHat een corrupte zip op :? (Zowel met de waarde "UTF8" als "ASCII"). Ik ben er wel achter gekomen dat als ik alleen bij 1 een (willekeurige) encoding opgeef en bij de rest niet, dat het dan op WinXP wel goed gaat, maar op RedHat nog steeds niet. Als ik alleen op 2 of 3 (of op allebei) de encoding meegeef, dan gaat het ook op WinXP niet goed.

Wie kan mij helpen? Dit domme gedoe kost me nu al vele uren en er lijkt niet echt een logisch patroon richting een oplossing in te zitten..

[ Voor 4% gewijzigd door Sammy op 24-05-2005 15:22 ]


Verwijderd

Ik snap niet helemaal waarom je eerst een string maakt van bout en daarna de bytes pakt. Misschien helpt het om toByteArray() van bout te gebruiken, dan heb je in ieder geval geen probleem met encoding.

Overigens maakt de constructor van ByteArrayOutputStream die jij gebruikt een stream met buffer size 32. Bij de beschrijving van toString in de API staat dat de hele buffer wordt vertaald naar String, niet alleen het gedeelte wat je hebt gebruikt... Het lijkt me een fout in de javadoc, maar het zou iets uit kunnen maken lijkt me.

Verder kun je natuurlijk ook gewoon helemaal geen ByteArrayOutputStream gebruiken, maar de ZipOutputStream direct om de FileOutputStream wrappen (afhankelijk van je toepassing natuurlijk).

Hopelijk heb je er wat aan.

  • Sammy
  • Registratie: Maart 2000
  • Laatst online: 03-05 10:41
Verwijderd schreef op dinsdag 24 mei 2005 @ 16:07:
Ik snap niet helemaal waarom je eerst een string maakt van bout en daarna de bytes pakt. Misschien helpt het om toByteArray() van bout te gebruiken, dan heb je in ieder geval geen probleem met encoding.
Daar heb je helemaal gelijk in, dat werkt ook perfect op beide platforms, dat was ik vergeten in mijn topicstart. Maarr... in mijn uiteindelijke oplossing ga ik de zipfile wegschrijven op de out (PrintWriter) van de HttpResponse in de servlet. Dat ziet er dan ongeveer zo uit:
Java:
1
2
3
4
5
6
7
8
9
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=" + request.getContextPath().substring(1) + ".zip");
        

        PrintWriter out = response.getWriter();
        
        out.print(zip); 
        out.flush(); 
        out.close();


Hier heb ik dus de stringrepresentatie van bout nodig.

Ik zie overigens nu in de servlet api dat ik ook getOutputStream() kan gebruiken en daar mijn bytes naar toe kan schrijven... eens kijken of dat werkt! Dan ben ik van het gezeur van encodings, hoewel ik nog steeds nieuwsgierig ben waarom dit nu fout gaat.
Verder kun je natuurlijk ook gewoon helemaal geen ByteArrayOutputStream gebruiken, maar de ZipOutputStream direct om de FileOutputStream wrappen (afhankelijk van je toepassing natuurlijk).
Dat kan dus niet, zie boven (ik heb geen FileOutputStream behalve in de test hier).
Hopelijk heb je er wat aan.
Ik ben weer tot nieuwe inzichten gekomen :)

update: de bout.toByteArray op de ServletOutputStream writen werkt, dus mijn probleem is opgelost, hoewel het natuurlijk interessant is waarom de byte[] de vertaalslag van en naar String niet overleeft.

[ Voor 8% gewijzigd door Sammy op 24-05-2005 16:41 ]


Verwijderd

ik kan je niet uitleggen waarom jouw code niet werkt, maar voel aan m'n water (en daar heb ik aardig wat van) dat het wel goed komt als je de ZipOutputStream om het resultaat van getOutputStream wrapt.