In diesem Beispiel wird gezeigt, wie einfache .zip-Dateien, die mehrere Dateien unterschiedlicher Dateitypen enthalten, gelesen werden. Hierfür werden die relevanten Daten aus den Metadaten jeder Datei extrahiert, jede Datei wird in ein ByteArray dekomprimiert und die Datei wird auf den Desktop geschrieben.
Die allgemeine Struktur von .zip-Dateien baut auf den Vorgaben von PKWARE Inc. auf, die Sie unter
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
finden. Zuerst kommen der Datei-Header und die Dateidaten für die erste Datei im .zip-Archiv, gefolgt vom Datei-Header und den Daten der folgenden Dateien. (Die Struktur des Headers wird weiter unten beschrieben.) Als Nächstes enthält die .zip-Datei einen optionalen Datendeskriptordatensatz (in der Regel, wenn die .zip-Datei im Arbeitsspeicher erstellt und nicht auf einem Datenträger gespeichert wurde). Dem folgen verschiedene weitere optionale Elemente: archive decryption header, archive extra data record, central directory structure, Zip64 end of central directory record, Zip64 end of central directory locator und end of central directory record.
Der Code in diesem Beispiel wurde so geschrieben, dass nur .zip-Dateien, die keine Ordner enthalten, analysiert werden. Datendeskriptordatensätze werden nicht erwartet. Sämtliche Informationen, die auf die letzten Dateidaten folgen, werden ignoriert.
Der Datei-Header der einzelnen Dateien folgt dem Format:
Datei-Header-Signatur
|
4 Byte
|
Erforderliche Version
|
2 Byte
|
Allgemeines Bit-Flag
|
2 Byte
|
Komprimierungsverfahren
|
2 Byte (8=DEFLATE; 0=UNCOMPRESSED)
|
Letzte Änderung Uhrzeit
|
2 Byte
|
Letzte Änderung Datum
|
2 Byte
|
crc-32
|
4 Byte
|
Komprimierte Dateigröße
|
4 Byte
|
Entkomprimierte Dateigröße
|
4 Byte
|
Dateinamenlänge
|
2 Byte
|
Zusatzfeldlänge
|
2 Byte
|
Dateiname
|
Variable
|
Feldlänge
|
Variable
|
Auf den Datei-Header folgen die eigentlichen Dateidaten, die je nach Komprimierungsverfahren-Flag komprimiert oder entkomprimiert sein können. Das Flag steht auf 0 (Null), wenn die Datei nicht komprimiert ist, auf 8, wenn die Daten mit dem deflate-Algorithmus komprimiert wurden, und auf einem anderen Wert für andere Komprimierungsalgorithmen.
Die Benutzeroberfläche für dieses Beispiel besteht aus einer Beschriftung und einem Textbereich (
taFiles
). Die Anwendung schreibt die folgenden Informationen in den Textbereich jeder Datei, die sie in der .zip-Datei findet: Dateiname, komprimierte Dateigröße und entkomprimierte Dateigröße. Das folgende MXML-Dokument definiert die Benutzeroberfläche für die Flex-Version der Anwendung:
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init();">
<mx:Script>
<![CDATA[
// The application code goes here
]]>
</mx:Script>
<mx:Form>
<mx:FormItem label="Output">
<mx:TextArea id="taFiles" width="320" height="150"/>
</mx:FormItem>
</mx:Form>
</mx:WindowedApplication>
Zu Beginn des Programms werden folgende Aufgaben ausgeführt:
-
Importieren der erforderlichen Klassen
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
Definition der Benutzeroberfläche für Flash
import fl.controls.*;
//requires TextArea and Label components in the Library
var taFiles = new TextArea();
var output = new Label();
taFiles.setSize(320, 150);
taFiles.move(10, 30);
output.move(10, 10);
output.width = 150;
output.text = "Contents of HelloAir.zip";
addChild(taFiles);
addChild(output);
-
Definition des ByteArrays
bytes
var bytes:ByteArray = new ByteArray();
-
Definition der Variablen für das Speichern von Metadaten aus dem Datei-Header
// variables for reading fixed portion of file header
var fileName:String = new String();
var flNameLength:uint;
var xfldLength:uint;
var offset:uint;
var compSize:uint;
var uncompSize:uint;
var compMethod:int;
var signature:int;
-
Definition der File-Objekte (
zfile
) und FileStream-Objekte (
zStream
) für die Darstellung der .zip-Datei und Angabe des Speicherorts der .zip-Datei, aus der die Dateien extrahiert werden - eine Datei mit dem Namen „HelloAIR.zip” im Desktop-Verzeichnis.
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
In Flex beginnt der Programmcode mit der Methode
init()
, die als
creationComplete
-Prozedur für das Stamm-Tag
mx:WindowedApplication
aufgerufen wird.
// for Flex
private function init():void
{
Das Programm beginnt mit dem Öffnen der .zip-Datei im Lesemodus.
zStream.open(zfile, FileMode.READ);
Es setzt dann die Eigenschaft
endian
von
bytes
auf
LITTLE_ENDIAN
, um festzulegen, dass bei der Byte-Reihenfolge numerischer Felder das niederwertigste Byte zuerst angegeben wird.
bytes.endian = Endian.LITTLE_ENDIAN;
Dann startet eine
while()
-Anweisung eine Schleife, die fortgesetzt wird, bis die aktuelle Position im Datenstrom der Dateigröße entspricht oder diese überschreitet.
while (zStream.position < zfile.size)
{
Die erste Anweisung in der Schleife liest die ersten 30 Byte des Dateistroms in das ByteArray
bytes
. Diese ersten 30 Byte bilden den ersten Datei-Header, dessen Größe vorgegeben ist.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
Danach liest der Code eine Ganzzahl (
signature
) aus den ersten Byte des 30-Byte-Headers. Durch die ZIP-Formatdefinition wird für die Signatur jedes Datei-Headers der Hexadezimalwert
0x04034b50
vorgegeben. Weicht die Signatur hiervon ab, ist der Code über den Dateiteil der .zip-Datei hinausgekommen und es sind keine weiteren Dateien zu extrahieren. In diesem Fall verlässt der Code die Schleife
while
sofort und wartet nicht auf das Ende des Byte-Arrays.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
Der nächste Abschnitt des Codes liest den Header-Byte an Offset-Position 8 und speichert den Wert in der Variablen
compMethod
. Dieses Byte enthält einen Wert, der das Komprimierungsverfahren angibt, das zur Komprimierung der Datei angewendet wurde. Es sind verschiedene Komprimierungsverfahren zulässig, praktisch verwenden jedoch nahezu alle .zip-Dateien den Komprimierungsalgorithmus „deflate“. Wurde die aktuelle Datei mit „deflate“ komprimiert, ist
compMethod
8; ist die Datei nicht komprimiert, ist
compMethod
0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
Den ersten 30 Byte folgt ein Header-Bestandteil von variabler Länge, der den Dateinamen und möglicherweise ein zusätzliches Feld enthält. In der Variablen
offset
ist die Größe dieses Bestandteils gespeichert. Die Größe wird durch Addition der Dateinamenlänge und der Länge des Zusatzfelds berechnet, die vom Header an den Offsets 26 und 28 eingelesen werden.
offset = 0; // stores length of variable portion of metadata
bytes.position = 26; // offset to file name length
flNameLength = bytes.readShort(); // store file name
offset += flNameLength; // add length of file name
bytes.position = 28; // offset to extra field length
xfldLength = bytes.readShort();
offset += xfldLength; // add length of extra field
Als Nächstes liest das Programm den Bestandteil des Datei-Headers mit variabler Länge ein, der die Anzahl der in der Variablen
offset
gespeicherten Byte angibt.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
Das Programm liest den Dateinamen aus dem Bestandteil des Datei-Headers mit variabler Länge und zeigt diesen im Textbereich zusammen mit der komprimierten (gezippten) und entkomprimierten (ursprünglichen) Größe der Datei an.
// Flash version
bytes.position = 30;
fileName = bytes.readUTFBytes(flNameLength); // read file name
taFiles.appendText(fileName + "\n"); // write file name to text area
bytes.position = 18;
compSize = bytes.readUnsignedInt(); // store size of compressed portion
taFiles.appendText("\tCompressed size is: " + compSize + '\n');
bytes.position = 22; // offset to uncompressed size
uncompSize = bytes.readUnsignedInt(); // store uncompressed size
taFiles.appendText("\tUncompressed size is: " + uncompSize + '\n');
// Flex version
bytes.position = 30;
fileName = bytes.readUTFBytes(flNameLength); // read file name
taFiles.text += fileName + "\n"; // write file name to text area
bytes.position = 18;
compSize = bytes.readUnsignedInt(); // store size of compressed portion
taFiles.text += "\tCompressed size is: " + compSize + '\n';
bytes.position = 22; // offset to uncompressed size
uncompSize = bytes.readUnsignedInt(); // store uncompressed size
taFiles.text += "\tUncompressed size is: " + uncompSize + '\n';
Im Beispiel wird der Rest der Datei für die durch die komprimierte Größe angegebene Länge aus dem Dateistrom in
bytes
gelesen und der Datei-Header in den ersten 30 Byte überschrieben. Die Angabe für die komprimierte Größe gilt auch dann, wenn die Datei nicht komprimiert wurde, da die komprimierte Größe in diesem Fall der entkomprimierten Größe der Datei entspricht.
// read compressed file to offset 0 of bytes; for uncompressed files
// the compressed and uncompressed size is the same
if (compSize == 0) continue;
zStream.readBytes(bytes, 0, compSize);
Im Beispiel wird dann die komprimierte Datei entkomprimiert und es wird die Funktion
outfile()
aufgerufen, um sie in den Ausgabedatenstrom zu schreiben.
outfile()
, der Dateiname und das Byte-Array, das die Dateidaten enthält, werden übergeben.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
Im vorigen Beispiel funktioniert
bytes.uncompress(CompressionAlgorithm.DEFLATE)
nur in AIR-Anwendungen. Um mit „deflate“ komprimierte Daten für AIR und Flash Player zu dekomprimieren, rufen Sie die
inflate()
-Funktion von ByteArray auf.
Die geschlossenen Klammern weisen auf das Ende der
while
-Schleife, der
init()
-Methode und des Flex-Anwendungscodes hin, mit Ausnahme der
outFile()
-Methode. Die Ausführung kehrt zum Beginn der Schleife
while
zurück und fährt mit der Verarbeitung der nächsten Byte in der .zip-Datei fort. Es wird eine weitere Datei extrahiert oder die Verarbeitung der .zip-Datei wird nach Verarbeitung der letzten Datei abgeschlossen.
} // end of while loop
} // for Flex version, end of init() method and application
Die Funktion
outfile()
öffnet eine Ausgabedatei im Schreibmodus auf dem Desktop und versieht sie mit dem durch den Parameter
filename
angegebenen Namen. Dann schreibt die Funktion die Dateidaten aus dem Parameter
data
in den Ausgabedatenstrom (
outStream
) und schließt die Datei.
// Flash version
function outFile(fileName:String, data:ByteArray):void
{
var outFile:File = File.desktopDirectory; // destination folder is desktop
outFile = outFile.resolvePath(fileName); // name of file to write
var outStream:FileStream = new FileStream();
// open output file stream in WRITE mode
outStream.open(outFile, FileMode.WRITE);
// write out the file
outStream.writeBytes(data, 0, data.length);
// close it
outStream.close();
}
private function outFile(fileName:String, data:ByteArray):void
{
var outFile:File = File.desktopDirectory; // dest folder is desktop
outFile = outFile.resolvePath(fileName); // name of file to write
var outStream:FileStream = new FileStream();
// open output file stream in WRITE mode
outStream.open(outFile, FileMode.WRITE);
// write out the file
outStream.writeBytes(data, 0, data.length);
// close it
outStream.close();
}