# Zip-Archive mit temporären Files erstellen



## K-Man (11. Sep 2012)

Hallo zusammen
Ich möchte eine zip-Datei erstellen und diese mit ebenfalls selbst erstellten Dateien füllen. Da man beim Erstellen von Files ja immer Pfade angeben muss, nehm ich temporäre Dateien und lösch diese am Ende der Prozedur. Da alles auf einem Tomcat laufen soll, nehm ich am Ende die delete(), statt deleteOnExit() Methode. Zu Beginn hab ich den Block mit dem BufferedInputStream nicht verwendet. Da hat dann alles geklappt, aber die Dateien im zip-Archive hatten dann keinen Inhalt. Mit dem BufferedInputStream-Block haben die Datein im zip dann ach den Inhalt "aStringX", nur werden sie jetzt nicht mehr gelöscht.
Was muss ich machen, um Dateien zu erstellen und mit Inhalt zu füllen und diese dann in ein neu erstelltes Zip-Archiv zu packen? Am Ende darf nur das zip-Archiv vorhanden sein.

Schon mal vielen Dank und schöne Grüße



```
ZipOutputStream zipOut = null;
BufferedInputStream bis = null;
try
{
            zipOut = new ZipOutputStream(new FileOutputStream("test.zip"));
            for (int i = 0; i < 10; i++)
            {
                File temp = File.createTempFile("test" + i, ".txt");
                BufferedWriter out = new BufferedWriter(new FileWriter(temp));
                out.write("aString" + i);
                out.close();

                String fileName = temp.getAbsolutePath();
                bis = new BufferedInputStream(new FileInputStream(fileName));
                int avail = bis.available();
                byte[] buffer = new byte[avail];
                if (avail > 0)
                {
                    bis.read(buffer, 0, avail);
                }

                ZipEntry ze = new ZipEntry(temp.getName());
                zipOut.putNextEntry(ze);
                zipOut.write(buffer, 0, buffer.length);
                zipOut.closeEntry();

                temp.delete();
            }
        }
```


----------



## SlaterB (11. Sep 2012)

bis.close();
vor dem delete, bzw. gleich in Zeile 21

-----

du kannst auch in einen ByteArray-Stream/ ein byte[] speichern statt auf die Festplatte,
falls nicht gerade MB-weise,
vielleicht gar direkt in den Zip-Stream, die temporären Dateien erscheinend jedenfalls unnötig


----------



## K-Man (11. Sep 2012)

Danke, ist eigentlich ganz logisch und peinlich es nicht selbst gesehen zu haben 

Hat aber geklappt und jetzt probier ich es einfach mal mit dem direkt in den Stream schreiben.

Danke.


----------



## K-Man (11. Sep 2012)

Eine Frage habe ich jetzt noch. Ich hab das jetzt mit dem ByteArrayOutputStream gemacht und es klappt auch alles wunderbar. Ich hab jetzt auch dem ZipOutputStream nur ein ByteArrayOutputStream zugewiesen. Ka ob das klappt. Jedenfalls muss jetzt nur noch eines geschehen. Das zip muss in einen String umgewandelt werden. Klingt komisch, aber es ist ein Webservice, der eigentlich einen String erwartet und daraus eine Datei erzeugt. Laut Entwickler kann es sich bei dem String auch um ein zip-Archiv handeln. Aber ich weiß jetzt nicht, wo ich da ansetzen soll


```
zipOut = new ZipOutputStream(new ByteArrayOutputStream());
            for (int i = 0; i < 10; i++)
            {
                String testStr = "Test" + i;
                ByteArrayInputStream bis = new ByteArrayInputStream(testStr.getBytes());
                int avail = bis.available();
                byte[] buffer = new byte[avail];
                if (avail > 0)
                {
                    bis.read(buffer, 0, avail);
                }
                if (bis != null)
                {
                    bis.close();
                }

                ZipEntry ze = new ZipEntry("Test" + i + ".csv");
                zipOut.putNextEntry(ze);
                zipOut.write(buffer, 0, buffer.length);
                zipOut.closeEntry();
            }
```


----------



## SlaterB (11. Sep 2012)

> testStr.getBytes()
ist relativ gefährlich, da muss nicht das raus kommen, was bei einem String auf Festplatte mit Default-Encoding gespeichert wird,
wobei wiederum auch nicht zwingend gesagt ist, ob jenes richtiger ist,
ich erinnere nur daran da aufzupassen,

falls das Zip auf der Festplatte gespeichert und zu normalen Dateien entpackt werden sollen, dann leistet
> BufferedWriter out = new BufferedWriter(new FileWriter(temp));
durchaus evtl. anderes, 
man würde ja auch nicht  BufferedOutputStream(FileOutputStream) + write(testStr.getBytes());
zum Speichern verwenden..,

bleibe lieber bei einem Writer, entweder BufferedWriter ganz oben oder darauf verzichten und nur OutputStreamWriter in einen ByteArrayOutputStream leiten um so am Ende ein byte[] mit dem String als Inhalt zu erhalten,

----

egal ob einfach nur testStr.getBytes() oder mit OutputStreamWriter, 
in jedem Fall musst du das erhaltene Array nicht erst wieder mit einem ByteArrayInputStream per Schleife einlesen für byte[] buffer,
du kannst das gesamte Array in einem Stück an zipOut.write() übergeben!

edit:
und ja noch die weitere Verkürzung ganz ohne Array:
den OutputStreamWriter direkt in zipOut leiten, falls da nicht close() usw. Probleme macht


------

was du übertragen solltst kann ich persönlich nun wirklich nicht beurteilen,
eine allgemeine Möglichkeit ist
Base64 ? Wikipedia


> Base64 ist ein Begriff aus dem Computerbereich und beschreibt ein Verfahren zur Kodierung von 8-Bit-Binärdaten (z. B. ausführbare Programme, ZIP-Dateien) in eine Zeichenfolge, die nur aus lesbaren Codepage-unabhängigen ASCII-Zeichen besteht.


aber dann muss natürlich die Gegenseite davon wissen, damit weiterarbeiten,
vielleicht ist es zufällig so, vielleicht nicht..


----------



## K-Man (11. Sep 2012)

Ok vielen Dank, dann schau ich es mir mal genauer an 

Danke für die schnelle und kompetente Hilfe. Großes Lob


----------



## freez (11. Sep 2012)

SlaterB hat gesagt.:


> Base64 ? Wikipedia
> 
> aber dann muss natürlich die Gegenseite davon wissen, damit weiterarbeiten,
> vielleicht ist es zufällig so, vielleicht nicht..



Ich hatte ein ähnliches Problem, dass ich die Gegenseite zwar verändern durfte aber die Schnittstelle / das Protokoll nur Texte erwartet und da bietet sich Base64 an. Die Filegröße wird zwar in Base64 um rund ein viertel (oder wars ein Drittel?) größer, aber dafür können Webservices (oder auch andere Textbasierte Services) sauber damit umgehen.


----------



## K-Man (17. Sep 2012)

Ich habe dazu eine generelle Frage. Ist es überhaupt möglich, ein zip in einen Bytecode zu verwandeln, diesen als Inhalt in eine csv-Datei zu schreiben und diese csv dann einfach in zip umzubenennen?
Das Problem ist ja, dass ich nur einen String abliefern kann. Der Entwickler meinte, ich könnte da auch einfach das zip-Archive als String übergeben. Das Probramm erzeugt danach zwar ein csv (mit dem Bytecode als Inhalt), aber man könne dies ja dann wieder in ein zip umwandeln, indem man die Endung ändert.
Hab es aber mit mehreren Varianten probiert, aber die zip lässt sich nicht mehr reproduzieren. Statt "new String(buffer)" hab ich auch schon "new BASE64Encoder().encode(buffer)" probiert, aber beides klappt nicht.


```
public static String fileToString(String file) {
        String result = null;
        DataInputStream in = null;

        try {
            File f = new File(file);
            byte[] buffer = new byte[(int) f.length()];
            in = new DataInputStream(new FileInputStream(f));
            in.readFully(buffer);
            result = new String(buffer);
        } catch (IOException e) {
            throw new RuntimeException("IO problem in fileToString", e);
        } finally {
            try {
                in.close();
            } catch (IOException e) { /* ignore it */
            }
        }
        return result;
    }
```

Der Entwickler meinte, der String wird direkt als HttpServletResponse verwendet, es findet keine Charsetkonvertierung statt.


```
HttpServletResponse response = getResponse( getExportFormat().getMimeType().getMimeId(), filename);
response.getWriter().write(content);
```


----------



## SlaterB (17. Sep 2012)

bytes direkt in String ist gefährlich, String kennt nur Chars, positive Zahlen zwischen 0 und 65.000, negative bytes werden in positive umgewandelt, gehen also kaputt

was an 
> new BASE64Encoder().encode(buffer)
nicht gehen soll musst du schon erklären,
fange die Übertragung bzw. vorher noch die lokalen Tests nicht mit MB an Daten an, sonsten ein Array von 5 Bytes, schön verteilt negativ, 0, positiv, 
übertrage das 'als String' auf welchem Wege auch immer, kommt am Ende dasselbe raus?

wenn ja dann auch auch mit den großen Daten bzw. den Netzwerkweg angehen

-----

in Schritten testen, genaue Beispiele, genauen Code, Ergebnisse und Probleme nennen, dann gehts voran, 
nicht nur 'klappt nicht'

zu deinem Code ist zu sagen, dass new String(buffer) wie schon früher String.getBytes() extrem gefährlich bis unmöglich ist


----------



## K-Man (17. Sep 2012)

Das erstellen des zip-Archives über Streams geht ja schon. Ich teste die Methoden erstmal lokal. Heißt ich kann zip öffnen und hab alle Dateien so wie sie sein sollen. Jetzt muss nur noch dieses zip in ein String umgeformt werden. Mit "es geht nicht" habe ich gemeint, dass ich ein csv bekomme, aber ich kann es nicht als zip öffnen. Also muss etwas mit mit der Methode "fileToString" nicht passen.
Ich hab dank deiner Hilfe schon herausgefunden, dass viele negative bytes vorkommen. Deswegen sollte nicht "getBytes" oder "new String(byte[])" verwendet werden, oder?

Ich denke bei mir ist schon das Problem, dass ich nicht weiß, wie ich das zip in welcher Art in ein String umwandeln muss, damit ich es später wieder als zip öffnen kann. Wenn ich mit "new String" oder "Base64Encoder" verwende, oder die Datei in ein binary verwandle, dann hab ich 3 komplett verschiedene Varianten, aber keine Ahnung, welche überhaupt zu einem Ergebnis führen kann. Ich versuch ja schon gar nicht erst den Netzwerk weg, sondern einfach File->String, String als Content für csv, csv in zip umbenennen, zip öffnen.

Aber danke für die Hilfe.


----------



## SlaterB (17. Sep 2012)

> Wenn ich mit "new String" oder "Base64Encoder" verwende, oder die Datei in ein binary verwandle, 
> dann hab ich 3 komplett verschiedene Varianten, aber keine Ahnung, welche überhaupt zu einem Ergebnis führen kann.

hmm, die dritte Variante verstehe ich nicht ganz, aber da hast du einerseits "new String", zu dem meine Warnung anscheinend angekommen ist und andererseits Base64Encoder, von mir nur Lob + Zitat


> Base64 ist ein Begriff aus dem Computerbereich und beschreibt ein Verfahren zur Kodierung von 8-Bit-Binärdaten (z. B. ausführbare Programme, ZIP-Dateien) in eine Zeichenfolge, die nur aus lesbaren Codepage-unabhängigen ASCII-Zeichen besteht.



tja, manchen könnte die Wahl dann leicht fallen, ich sage nichts neues mehr dazu außer dem zwischen den Zeilen hier 

wie es nach der Entscheidung weitergeht, welchen Aufbau, welche Fehler usw., ist nach wie vor offen


----------



## K-Man (17. Sep 2012)

Base64 hab ich mir ja schon zu herzen genommen 

Ich glaube das Problem liegt woanders. Mit Base64 kann ich ein byte[] in ein String umwandeln und wieder zurück und die beiden byte[] sind zu 100% identisch. Nur kann ich damit kein zip abbilden oder ich versteh es falsch.

Heißt es geht wohl erst nur darum, wie ich ein zip in ein String umwandeln kann, sodass ich später den String in eine Datei schreiben kann und diese Datei dann wieder in ein zip umbenenne und dann das ursprüngliche zip erhalte. Die Frage ist ja schon, ob sowas überhaupt möglich ist :/
Auf dem Server findet ja keine Decodierung statt, sondern der String wird einfach als Inhalt in eine Datei geschrieben.

Sorry, wenn ich nerve, aber ich steh da gerade auf dem Schlauch


----------



## SlaterB (17. Sep 2012)

wie gesagt


> aber dann muss natürlich die Gegenseite davon wissen, damit weiterarbeiten,


das ist Voraussetzung, 
wenn die Serverseite einen String in eine Datei schreibt gibt es keinen Weg auf dieser Welt, das als Zip zu verwenden,
(edit: keinen mir bekannten, theoretisch nutzt ein Text ja auch alle Kombinationen die denkbar sind, 
irgendwelche hohen unlesbaren chars sorgen sicher auch die Byte-Kombinationen, die als byte eingelesen negativ wären,
das müsste man Zeichen für Zeichen einzeln testen, wahrscheinlich sogar auf 2 Byte-Kombinationen achten,
hängt auch davon ab, wie der Server den String abspeichert, mit welchen Encoding,

dass es das nicht fertig gibt, oder zumindest nicht mir bekannt, läßt mich vermuten dass es nicht gerade als normal praktikabel angesehen wird, aber 'kein Weg der Welt' kann ich doch nicht mit Überzeugung behaupten)

der übertragene String muss erst ins byte[] dekodiert werden


----------



## K-Man (17. Sep 2012)

Von Serverseite hab ich nur diese Information. Vllt geht es wirklich nicht?

der String wird direkt als HttpServletResponse verwendet, es findet keine Charsetkonvertierung statt.

```
HttpServletResponse response = getResponse( getExportFormat().getMimeType().getMimeId(), filename);
response.getWriter().write(content);
```


----------



## SlaterB (17. Sep 2012)

das ist Code von deinem Senden, oder?
auf deiner Seite kannst du nicht mehr machen als einen String auf geeigneste Weise zu übertragen, fertig,
od der auf der Gegenseite direkt zu einer Datei werden kann ist für mich sehr fraglich, was neues zu sagen ist hier aber nicht

da dein Posting irgendwie nach direkter Antwort klingt, nutze ich den Platz noch für 
> Laut Entwickler kann es sich bei dem String auch um ein zip-Archiv handeln.

kannst du mit diesem Menschen darüber reden oder ist das ein Zitat aus einer Doku, kannst du diese ausführlicher posten/ Link?



abgesehen davon bei zukünftigen Postings von dir damit rechnen 
dass ich nicht mehr antworte, sofern ich wie hier praktisch nichts neues beitragen kann


----------



## ssoul26 (17. Sep 2012)

Zur Erzeugung eines Strings aus einem File versuch mal folgendes: 


```
private static String leseDatei(String sPfad) throws IOException {
      //InputStream erzeugen
      FileInputStream Fis = new FileInputStream(new File(sPfad));
      //Datenkanal
      FileChannel Fk = Fis.getChannel();
      //Bytes mappen
      MappedByteBuffer Mbp = Fk.map(FileChannel.MapMode.READ_ONLY, 0, Fk.size());
      //Dekoder verwenden
      Fis.close();
      return Charset.defaultCharset().decode(Mbp).toString();
   }
```


----------



## K-Man (17. Sep 2012)

@ssoul26:
Das ist es fast. Damit schaut die Datei vom Inhalt her fast wie das original Zip aus. Nur ein paar Zeichen sind noch unterschiedlich, liegt wohl am Charset? Ich teste es jedenfalls lokal. zB macht er noch aus einem › aus dem Original ein �
Aber ich frage mich dennoch noch, ob das überhaupts möglich ist. Wenn ich ein zip in ein txt umbenenne und den Inhalt in eine andere Datei kopiere, dann habe ich ja auch kein zip (oder gehen mit Notepad chars verloren?).

@SlaterB:
Trotzdem vielen Dank. Wahrscheinlich geht es auch nicht. Ich wüsste ja jetzt auch nicht, wie das mit dem zip funktionieren soll, indem man eine Stringrepräsentation eines zips als Inhalt für eine andere Datei verwendet und diese dann als zip weiterverwenden kann.


----------

