# Einer Zip Datei ein leeres Verz. hinzufügen



## DarthShader (16. März 2007)

Hallo,

ich habe z.Z. ein kleines Problem, was das handling von Zip-Files in Java angeht. Ich kann sehr leicht ein Zip File erstellen und Dateien hinzufügen (Stichwort ZipOutputStream, ZipEntry etc...).

Aber: ich würde dem Zip File gerne ein Verzeichnis hinzufügen, und zwar ein leeres.

Ich habe das Gefühl, dass die ZipEntrys nur Dateien sein können. Ich hoffe, dass ich damit nicht recht habe.

Kann mir jemand helfen?


Danke!


----------



## MeinerEiner_80 (19. März 2007)

Moin!
So gehts:

```
ZipOutputStream zout = new ZipOutputStream(new FileOutputStream("F:/test.zip"));
            ZipEntry e = new ZipEntry("Test/");
            zout.putNextEntry(e);
            zout.closeEntry();
            zout.close();
```


*grüssle*
MeinerEiner


----------



## Thomas Darimont (19. März 2007)

Hallo,



> Ich habe das Gefühl, dass die ZipEntrys nur Dateien sein können. Ich hoffe, dass ich damit nicht recht habe.



nein du kannst auch (leere) Verzeichnisse zippen. Das Problem dabei ist nur, dass einige Packprogramme wie WinZip scheinbar nicht richtige mit leeren Verzeichnissen umgehen können... (sprich der leere Ordner ist im Zip wird aber nicht angezeigt)

Gruß Tom


----------



## DasArne (23. September 2009)

Hallo zusammen,
ich muss dieses Thema nochmal aufgreifen.
Die meisten legen für Ordner erst gar keine ZipEntries an, und fahren damit sicher mit den meisten Zip-Programmen gut. Ich möchte aber Zip-Folder kommentieren. Das Kommandozeilentool zip kann das mit der Option -c also geht das prinzipiell. 
Ich versuche das folgender maßen:

```
package org.example.zip;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;

public class ZipTest {
        public static void main (String argv[]) {
                try {
                        FileOutputStream dest = new FileOutputStream("/tmp/checkFolder.zip");
                        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));

                        ZipEntry folderEntry = new ZipEntry("TestFolder");
                        folderEntry.setComment("My Folder comment.");
                        out.putNextEntry(folderEntry);
                        out.closeEntry();
                        
                        ZipEntry fileEntry = new ZipEntry("TestFolder/TestFile.txt");
                        fileEntry.setComment("My File comment.");
                        out.putNextEntry(fileEntry);
                        out.write("Hallo Zip!".getBytes());
                        out.closeEntry();
                        
                        out.close();
                } catch(Exception e) {
                        e.printStackTrace();
                }
        }
}
```
Wenn ich mir das mit unzip anschaue sieht das zuerst gut aus:

```
Archive:  /tmp/checkFolder.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
        0  09-22-09 15:09   TestFolder
My Folder comment.
       10  09-22-09 15:09   TestFolder/TestFile.txt
My File comment.
 --------                   -------
       10                   2 files
```
Das mit Windows ausgeliefert "eingebaute" Zip findet leider immer leere Dateien mit dem Namen des Ordners und unzip hat natürlich ein Problem sowas auszupacken:

```
unzip  /tmp/checkFolder.zip                                                       
Archive:  /tmp/checkFolder.zip                                                                       
  inflating: TestFolder                                                                              
checkdir error:  TestFolder exists but is not directory                                              
                 unable to process TestFolder/TestFile.txt.
```
Kann man nicht doch irgendwie einem ZipEntry sagen, das es ein Verzeichnis ist?
Wie kann man ein Verzeichnis mit Kommentar anlegen?

Viele Grüße
 Arne


----------



## Thomas Darimont (23. September 2009)

Hallo,

wenn ich mir hiermit mal ein Zip File generieren lasse und mir anschließend das Zip mit der Windows Zip Unterstützung anschaue, sehe ich 

"test1" als Datei
"test2" als Ordner und darin die Datei "test3".

Wenn ich dieses zip jedoch entpacke bekomme ich korrekte Verzeichnisse angelegt. 

Deshalb die Frage: Ist es wirklich so schlimm, wenn du mit der Windows Zip Unterstützung die Verzeichnisse nicht richtig siehst? Schließlich sind sie ja da wenn man das zip entpackt.


```
public class ZipWithEmtpyDirectoriesExample {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception{
        ZipOutputStream zip = new ZipOutputStream(new FileOutputStream("/tmp/zipWithEmptyFolders.zip"));
        zip.putNextEntry(new ZipEntry("test1/"));
        zip.putNextEntry(new ZipEntry("test2/test3/"));
        zip.closeEntry();
        zip.close();
    }

}
```

Gruß Tom


----------



## DasArne (23. September 2009)

Hi Tom,
danke Dir für Deine Antwort. Natürlich ist es kein Beinbruch wenn mir ein Windows-Zip ein Verzeichnis und eine Datei mit dem selben Namen anzeigt. Bis auf das Kommando-Zeilentool unzip scheinen sowieso alle zip-Programme sich sehr viel Mühe zu geben ohne Fehlermeldungen zip-Files auszupacken.
Aber das ZipEntry hat ja immerhin die Methode isDirectory() . Und das nicht ohne Grund: Das auspackende Programm muss da einen Ordner anlegen. Wie wird dieser Boolean bestimmt? Ist ein leerer Eintrag immer ein Folder? Wo ist der Setter? 
Ich habe die apache-zip-utils aus dem ant.jar genommen, weil man damit das Encoding für Dateinamen und Kommentare setzen kann. (Kann ich nur jedem ans Herz legen. Achtung unterschiedlich Encoding für unterschiedliche Plattformen: Windows braucht "Cp437" der Rest der Welt "UTF-8") 
Im Java-Doc vom ZipOutputStream steht dazu:


> Reimplementation of java.util.zip.ZipOutputStream that does handle the extended functionality of this package, especially internal/external file attributes and extra fields with different layouts for local file data and central directory entries.


Könnten dieses isDirectory nicht ein "especially internal/external file attributes" sein? 
Leider wird im Weiteren nicht mehr darauf eingegangen? 
Tom hast Du schon mal was mit diesen "file attributes" gemacht? 
Weißt Du was die meinen?

Viele Grüße
 Arne


----------



## Thomas Darimont (23. September 2009)

Hallo,



> Aber das ZipEntry hat ja immerhin die Methode isDirectory() . Und das nicht ohne Grund: Das auspackende Programm muss da einen Ordner anlegen. Wie wird dieser Boolean bestimmt? Ist ein leerer Eintrag immer ein Folder? Wo ist der Setter?



In der Apache Ant Variante von ZipEntry ist die isDirectory Methode folgendermaßen implementiert:

```
public boolean isDirectory()
  {
    return getName().endsWith("/");
  }
```

... also nicht als "Special Attribut" sondern eher als eine Namenskonvention.


Gruß Tom


----------



## DasArne (24. September 2009)

Hi Thomas,
ja, ein scharfer Blick in den Source kann ja Berge versetzen.   Danke Dir Thomas!  
Unzip lässt sich mit mit Deinem Tipp tatsächlich dazu überreden das Zip-File auszupacken. 
Mein gewünschtes explizites Setzen der Art eines ZipEntries, kann (und muss - meiner Meinung nach) dennoch durchgeführt werden. 
Man muss das 4. Bit der External-Atrributes setzen! Ist dieses Bit an, ist es ein Verzeichnis, ist es aus, handelt es sich um eine Datei. 
Hier nochmal das komplette Beispiel zum Mitschreiben: 

```
package org.example;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;

public class ZipTest {

	public final static long ZIPENTRY_IS_DIRECTORY = 1<<4;
	public final static String WINDOW_ZIP_ENCODING="Cp437";
	public final static String NON_WINDOW_ZIP_ENCODING="UTF-8";
	
	public static void main (String argv[]) {
		try {
            // Bitmask indicating directories in 'external attributes' of a ZIP archive entry.

			FileOutputStream dest = new FileOutputStream("/tmp/checkFolder.zip");
			ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
			out.setEncoding(WINDOW_ZIP_ENCODING);
			
			ZipEntry folderEntry = new ZipEntry("TestFolder/");
			folderEntry.setExternalAttributes(folderEntry.getExternalAttributes()|ZIPENTRY_IS_DIRECTORY);
			folderEntry.setComment("My Folder comment.");
			out.putNextEntry(folderEntry);
			out.closeEntry();
			
			ZipEntry fileEntry = new ZipEntry("TestFolder/TestFile.txt");
			fileEntry.setComment("My File comment.");
			out.putNextEntry(fileEntry);
			out.write("Hallo Zip!".getBytes());
			out.closeEntry();
			
			out.close();
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}
```

Nun kann auch Windows-Zip was damit anfangen.
Alternativ kann man auch sinniger Weise den Unixmode setzen.  Der rechnet nämlich ganz neben bei ein komplettes ExternalAttribute zusammen. 
Natürlich für Verzeichnisse nur richtig, wenn Dein Slash am Ende des Verzeichnis-Namens steht.  

```
public void setUnixMode(int mode) {
        setExternalAttributes((mode << 16)
                              // MS-DOS read-only attribute
                              | ((mode & 0200) == 0 ? 1 : 0)
                              // MS-DOS directory flag
                              | (isDirectory() ? 0x10 : 0));
        platform = PLATFORM_UNIX;
    }
```
Das sollte dann zu allen möglichen Plattformen kompatible sein.

Viele Grüße
 Arne


----------



## Thomas Darimont (24. September 2009)

Hallo,

coole Sache, danke für den Tipp 

Gruß Tom


----------

