# Zip entpacken



## Snape (20. Mrz 2012)

Tach,
ich finde meinen Denkfehler nicht. Beim zweiten Durchgang fliegt mir die besagte IOException: Stream closed um die Ohren:


```
public void extractArchiveInOneDirectory(File archiveFile, File destinationDirectory) throws Exception { 
        if (!destinationDirectory.exists()) { 
            destinationDirectory.mkdir(); 
        } 
 
        ZipFile zipFile = new ZipFile(archiveFile); 
        Enumeration entries = zipFile.entries(); 
 
        byte[] buffer = new byte[16384]; 
        int len; 
 
        while (entries.hasMoreElements()) { 
            ZipEntry entry = (ZipEntry) entries.nextElement(); 
 
            String entryFileName = entry.getName(); 
 
            if (!entry.isDirectory()) { 
                // nur Dateiname, Pfad wegwerfen 
                boolean slashIndexFound = false; 
                int slashIndex = entryFileName.lastIndexOf("/"); 
                if ( slashIndex == -1 ){ 
                    slashIndex = entryFileName.lastIndexOf("\"); 
                    if ( slashIndex != -1 ) 
                        slashIndexFound = true; 
                } else { 
                    slashIndexFound = true; 
                } 
                if ( slashIndexFound ){ 
                    entryFileName = entryFileName.substring(slashIndex + 1, entryFileName.length()); 
                    slashIndexFound = false; 
                } 
 
                BufferedOutputStream bos = new BufferedOutputStream( 
                        new FileOutputStream(new File(destinationDirectory, entryFileName))); 
 
                BufferedInputStream bis = new BufferedInputStream(zipFile 
                        .getInputStream(entry)); 
 
               // HIER  =================================
                while ((len = bis.read(buffer)) > 0) {
                    bos.write(buffer, 0, len); 
                } 
 
                saveLogEntry("Extrahiere Datei " + entryFileName + " ins Verzeichnis " + destinationDirectory); 
 
                bos.flush(); 
                bos.close(); 
                bis.close(); 
            } 
        } 
        zipFile.close(); 
    }
```
Selbst wenn ich die
                bos.flush(); 
                bos.close(); 
                bis.close(); 
auskommentiere, bekomme ich den Fehler. Woran denke ich nicht?


----------



## SlaterB (20. Mrz 2012)

wenn du dicke Arrays einliest, brauchst du nicht wirklich Buffer-Streams,

funktioniert Code a la
Java Tips - How to extract file/files from a zip file
?
dort gibts ' zipinputstream.closeEntry();' allerdings auch generell etwas anderen Aufbau,
'java extract zip' liefert über Suchmaschinen bestimmt weitere getestete Beispiele, vielleicht noch nach ZipFile suchen,

verwendest du ein kleines Zip-File, welches du zum Testen hochladen könntest? (Anhang an Posting)
wenn das schlecht aufgebaut ist mag auch bei besten Code ein Fehler kommen, vielleicht aber ein anderer

------

erstaunlich dass du Code mit nicht kompilierbaren "\" postest, die Code-Darstellung im Forum bemerkt das auch


----------



## irgendjemand (20. Mrz 2012)

das syntax-highlight verrät schon alles ...

[c]slashIndex = entryFileName.lastIndexOf("\");[/c]

da sollte eigentlich schon der compiler meckern ...
wenn überhaupt dann allerhöchstens so

[c]slashIndex = entryFileName.lastIndexOf("\\");[/c]

btw : backslashes gibt es innerhalb eines jar nicht ... sondern nur normale foreward-slashes ... die zeile kann also eigentlich komplett raus


----------



## Snape (21. Mrz 2012)

SlaterB hat gesagt.:


> wenn du dicke Arrays einliest, brauchst du nicht wirklich Buffer-Streams,


Das werde ich wohl nie begreifen, wann man mit FileOutput.., Buffered.... und Streams arbeitet. :bahnhof:
Was mich irritiert hat: Der Code funktioniert auf meiner Entwicklerbüchse, aber nicht auf dem Zielrechner. OK, meine Testzip hat nur wenige Verzeichnisse und Einträge...



SlaterB hat gesagt.:


> funktioniert Code a la
> Java Tips - How to extract file/files from a zip file
> ?
> dort gibts ' zipinputstream.closeEntry();' allerdings auch generell etwas anderen Aufbau,


Nicht wirklich. Ich will ab dem Verzeichnis, in welchem sich das zip-Archiv befindet, die darin enthaltenen Dateien mit den entsprechenden Pfaden entpacken. Also wenn in D:\temp eine archiv.zip liegt, welche dok1.pdf im subdir "customer1" und dok2.pdf im subdir "customer2/export" enthält, soll dann das hier bei heraus kommen:
D:\temp\customer1\dok1.pdf
D:\temp\customer2\export\dok2.pdf
Aber bei fileOutputStream = new FileOutputStream(... fliegt mir eine FileNotFoundException um die Ohren - Code s.u.



SlaterB hat gesagt.:


> erstaunlich dass du Code mit nicht kompilierbaren "\" postest, die Code-Darstellung im Forum bemerkt das auch


Ist wohl beim paste hier verloren gegangen. Im Code steht natürlich ein doppelter Backslash.

Aktueller nicht funktionierender Code.

```
public void getZipFiles(File archiveFile, File destinationDirectory){
        try{
            if (!destinationDirectory.exists()) {
                destinationDirectory.mkdir();
            }
            String destinationName = destinationDirectory.getAbsolutePath();
//            String destinationname = "d:\\servlet\\testZip\\";
            byte[] buf = new byte[1024];
            ZipInputStream zipInputStream = null;
            ZipEntry zipEntry;
            zipInputStream = new ZipInputStream(
                    new FileInputStream(archiveFile));

            zipEntry = zipInputStream.getNextEntry();
            while (zipEntry != null) {
                //for each entry to be extracted
                String entryName = zipEntry.getName();
                System.out.println("entryname: " + entryName);
                int n;
                FileOutputStream fileOutputStream;
                File newFile = new File(entryName);
                String directory = newFile.getParent();

                if(directory == null){
                    if(newFile.isDirectory())
                        break;
                }

                // Hier knallt es jetzt mit einer FileNotFoundException
                fileOutputStream = new FileOutputStream(
                   destinationName + "/" + entryName);

                while ((n = zipInputStream.read(buf, 0, 1024)) > -1)
                    fileOutputStream.write(buf, 0, n);

                fileOutputStream.close();
                zipInputStream.closeEntry();
                zipEntry = zipInputStream.getNextEntry();

            }//while

            zipInputStream.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
```


----------



## SlaterB (21. Mrz 2012)

nicht gefundene Verzeichnisse sollten wohl weniger ein Problem sein, das kannst und musst du gegebenenfalls anpassen,
aber funktionieren die Streams?

alternativ immer noch die Frage, ein Zip-File bereitzustellen

----

ich habe es nun aber auch selber mit mysql-connector-java-5.1.17.zip versucht,
dein Code vom ersten Posting läuft ohne Fehler, schreibt aber alle Dateien in ein Verzeichnis

der von mir gepostete Link macht es wohl wirklich nicht sehr schlau, hier eine für mich zufriedenstellende Version:

```
public class Test
{
    public static void main(String[] args)
    {
        getZipFiles(new File("c:/temp/mysql-connector-java-5.1.17.zip"), new File("c:/temp/"));
    }

    public static void getZipFiles(File archiveFile, File destinationDirectory)
    {
        try
        {
            if (!destinationDirectory.exists())
            {
                destinationDirectory.mkdir();
            }
            String destinationName = destinationDirectory.getAbsolutePath();
            // String destinationname = "d:\\servlet\\testZip\\";
            byte[] buf = new byte[1024];
            ZipInputStream zipInputStream = null;
            ZipEntry zipEntry;
            zipInputStream = new ZipInputStream(new FileInputStream(archiveFile));

            zipEntry = zipInputStream.getNextEntry();
            while (zipEntry != null)
            {
                // for each entry to be extracted
                String entryName = zipEntry.getName();
                System.out.println("entryname: " + entryName);
                File nf = new File(destinationName + "/" + entryName);
                if (entryName.endsWith("/"))
                {
                    nf.mkdir();
                }
                else
                {
                    FileOutputStream fileOutputStream = new FileOutputStream(nf);

                    int n;
                    while ((n = zipInputStream.read(buf, 0, 1024)) > -1)
                        fileOutputStream.write(buf, 0, n);

                    fileOutputStream.close();
                    zipInputStream.closeEntry();
                }
                zipEntry = zipInputStream.getNextEntry();
            }// while

            zipInputStream.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}
```


----------



## Snape (21. Mrz 2012)

SlaterB hat gesagt.:


> nicht gefundene Verzeichnisse sollten wohl weniger ein Problem sein, das kannst und musst du gegebenenfalls anpassen


Ich raff nicht, wieso der mir die FileNotFoundException "(Das System kann den angegebenen Pfad nicht finden)" um die Ohren schmeisst. Natürlich existiert der Zielpfad für die zu extrahierende Datei aus dem ZIP-File nicht, den muss das Programm anlegen. Wieso das nicht passiert, k.A., dafür kenne ich den FileOutputStream nicht gut genug. Denn in Deinem Code kracht es wie bei mir bei


```
FileOutputStream fileOutputStream = new FileOutputStream(nf);
```



SlaterB hat gesagt.:


> aber funktionieren die Streams?


Was meinst Du damit, wie soll ich das erkennen?



SlaterB hat gesagt.:


> alternativ immer noch die Frage, ein Zip-File bereitzustellen


Anbei.


----------



## SlaterB (21. Mrz 2012)

Snape hat gesagt.:


> Natürlich existiert der Zielpfad für die zu extrahierende Datei aus dem ZIP-File nicht, den muss das Programm anlegen.


sollte in meinem Programm klappen, die Bedingung ist ja auch erkennbar,
ok, das kann davon abhängen, ob Verzeichnisse überhaupt als eigene Einträge vorkommen, 
da reagiert Zip machmal unterschiedlich, glaube ich von anderen Themen zu erinnern,

statt mit meinem Code ansonsten von jedem Ziel-File den Parent bestimmen und fürs Parent mkdirs() ausführen,
also ganz einfach dafür sorgen dass das Verzeichnis da ist
(bevor du mutmaßlich gleich 'häh?' fragst, in Ruhe überlegen, was jeweils gemeint ist, was ist das Ziel-File, welche Möglichkeiten kommen zumindest in Betracht, was ist der Parent eines Files usw.)



Snape hat gesagt.:


> Was meinst Du damit, wie soll ich das erkennen?


okok, das mit den Verzeichnissen ist wirklich nicht ganz trivial und muss vorher behandelt werden,
erst danach ist evtl. wieder an die ursprüngliche Fehlermeldung zu denken


----------



## Snape (21. Mrz 2012)

Danke erst mal. Soweit funktioniert es.
Bis auf eine Kleinigkeit: In der Zeile


```
zipEntry = zipInputStream.getNextEntry();
```

fliegt mir in einer zip-Datei eine



> java.lang.IllegalArgumentException
> at java.util.zip.ZipInputStream.getUTF8String(ZipInputStream.java:307)
> at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:247)
> at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:74)
> ...


um die Ohren. Und zwar direkt beim ersten Entry. Wie kann ich herausfinden, welcher der erste Entry ist und was daran nicht utf-8-konform ist?


----------



## SlaterB (21. Mrz 2012)

eine solche Fehlermeldung kann man recht gut suchen (IllegalArgumentException ZipInputStream.getUTF8String),
die Einträge sehen aber ziemlich alt aus, benutzt du eine Java-Version aus grauer Vorzeit?

der Eintrag dürfte doch der erste sein, den auch normale Zip-Programme anzeigen, alphabetisch sortiert meinetwegen,
wenn Verzeichnisse für sich nicht aufgeführt werden (an anderen Zips zu testen bzw. bisher ja das Problem), dann eben die erste Datei,
deren Dateiname enthält Sonderzeichen

ob es eine Lösung gibt? wenn du nichts findest kann ich auch nochmal nachschauen,
direkt weiß ich nichts


----------



## irgendjemand (22. Mrz 2012)

SlaterB hat gesagt.:


> ```
> // ...
> String destinationName = destinationDirectory.getAbsolutePath();
> // ...
> ...



ist aber nicht gerade die feine englische art ...
man sollte lieber den konstruktor File(File, String) verwenden ...

würde dann so aussehen


```
File nf=new File(destinationDirectory, entryName);
```

ist IMO weniger fehleranfällig da getAbsolutePath unter win pfad mit "\" liefert ... du aber dann manuell einen "/" addest und der dateiname aus dem jar auch "/" enthält ... > misch-masch ...
gibt zwar an sich keinen fehler da win zum glück beides versteht ... aber wenn es die api anbietet sollte man schon bei File-objekt bleiben ...


ob man das ganze nun iterativ löst oder rekursiv ... hmm .. gerade bei größeren archiven würde die rekursive methode schnell in einen stackoverflow rennen ...


----------



## Snape (22. Mrz 2012)

SlaterB hat gesagt.:


> die Einträge sehen aber ziemlich alt aus, benutzt du eine Java-Version aus grauer Vorzeit?


Na soooo alt ist die 1.6er auch nicht.



SlaterB hat gesagt.:


> wenn Verzeichnisse für sich nicht aufgeführt werden (an anderen Zips zu testen bzw. bisher ja das Problem), dann eben die erste Datei,
> deren Dateiname enthält Sonderzeichen
> 
> ob es eine Lösung gibt? wenn du nichts findest kann ich auch nochmal nachschauen,
> direkt weiß ich nichts


Es ist tatsächlich so banal, dass die zu entpackende Datei ein Sonderzeichen (Umlaut ü) enthält. Ich dachte, das sei alles schön Unicode-konform und kein Problem?
Bei wer-weiss-was hatte schon mal jemand vor 11 Jahren (!) das gleiche Problem ohne Lösung Zip-Probleme mit Umlauten | aus Forum Java | wer-weiss-was
Handelt sich offenbar um einen noch immer nicht gelösten Bug im JDK...


----------



## Snape (22. Mrz 2012)

Hab's jetzt mit TrueZip erledigt.


----------

