In dit voorbeeld ziet u hoe een eenvoudig ZIP-bestand wordt gelezen dat verschillende typen bestanden bevat. Hiervoor worden relevante gegevens uit de metagegevens van elk bestand opgehaald, waarbij elk bestand in een bytearray wordt gedecomprimeerd en naar de desktop wordt geschreven.
De algemene structuur van een ZIP-bestand is gebaseerd op de specificatie van PKWARE Inc., die u kunt vinden op
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
. Eerst staan er een bestandsheader en bestandsgegevens voor het eerste bestand in het ZIP-bestand, gevolgd door een combinatie van een bestandsheader en bestandsgegevens voor elk ander bestand. (De structuur van de bestandsheader wordt later beschreven.) Daarna bevat het ZIP-bestand eventueel een record met een gegevensdescriptor (gewoonlijk wanneer het ZIP-bestand in het geheugen is gemaakt in plaats van opgeslagen op een schijf). Hierna volgen diverse optionele elementen: decoderingsheader van archief, extra gegevensrecord voor archief, centrale mapstructuur, Zip64-einde van centrale maprecord, Zip64-einde van centrale maplocator en het einde van de centrale maprecord.
De code in dit voorbeeld is alleen geschreven om ZIP-bestanden te parseren die geen mappen bevatten en er worden geen records met een gegevensdescriptor verwacht. Alle informatie na de laatste bestandsgegevens wordt genegeerd.
De bestandsheader voor elk bestand heeft de volgende indeling:
handtekening van bestandsheader
|
4 bytes
|
vereiste versie
|
2 bytes
|
algemene bitmarkering
|
2 bytes
|
compressiemethode
|
2 bytes (8=DEFLATE; 0=UNCOMPRESSED)
|
tijd van laatste bestandswijziging
|
2 bytes
|
datum van laatste bestandswijziging
|
2 bytes
|
crc-32
|
4 bytes
|
gecomprimeerde grootte
|
4 bytes
|
ongecomprimeerde grootte
|
4 bytes
|
lengte van bestandsnaam
|
2 bytes
|
lengte van extra veld
|
2 bytes
|
bestandsnaam
|
variabele
|
extra veld
|
variabele
|
Na de bestandsheader volgen de werkelijke bestandsgegevens die gecomprimeerd of niet kunnen zijn, afhankelijk van de markering voor de compressiemethode. De markering is 0 (nul) als de bestandsgegevens niet zijn gecomprimeerd, 8 als de gegevens zijn gecomprimeerd met de Deflate-algoritme of een andere waarde voor andere compressiealgoritmen.
De gebruikersinterface voor dit voorbeeld bestaat uit een label en een tekstgebied (
taFiles
). De toepassing schrijft de volgende informatie naar het tekstgebied voor elk bestand dat in het ZIP-bestand wordt aangetroffen: bestandsnaam, gecomprimeerde grootte en niet-gecomprimeerde grootte. Het volgende MXML-document bepaalt de gebruikersinterface voor de Flex-versie van de toepassing:
<?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>
Aan het begin van het programma worden de volgende taken uitgevoerd:
-
De vereiste klassen worden geïmporteerd.
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
De gebruikersinterface wordt gedefinieerd. voor 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);
-
De bytearray
bytes
wordt gedefinieerd.
var bytes:ByteArray = new ByteArray();
-
Er worden variabelen gedefinieerd voor het opslaan van metagegevens uit de bestandsheader.
// 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;
-
De objecten File (
zfile
) en FileStream (
zStream
) worden gedefinieerd voor het ZIP-bestand en de locatie wordt opgegeven van het ZIP-bestand waaruit de bestanden worden opgehaald: een bestand met de naam “HelloAIR.zip” in de desktopmap.
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
In Flex start de programmacode in de
init()
-methode, die wordt opgeroepen als de
creationComplete
-handler voor de root van de
mx:WindowedApplication
-tag.
// for Flex
private function init():void
{
Het programma begint met het openen van het ZIP-bestand in de leesmodus.
zStream.open(zfile, FileMode.READ);
Vervolgens wordt de eigenschap
endian
van
bytes
op
LITTLE_ENDIAN
ingesteld om aan te geven dat de minst significante byte eerst wordt aangegeven in de bytevolgorde van numerieke velden.
bytes.endian = Endian.LITTLE_ENDIAN;
Vervolgens wordt met de instructie
while()
een lus gestart die wordt uitgevoerd tot de huidige positie in de bestandsstroom groter is dan of gelijk is aan de grootte van het bestand.
while (zStream.position < zfile.size)
{
Met de eerste instructie in de lus worden de eerste 30 bytes van de bestandsstroom ingelezen in de bytearray
bytes
. De eerste 30 bytes bevatten het gedeelte van de eerste bestandsheader dat een vaste grootte heeft.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
Vervolgens wordt een geheel getal (
signature
) gelezen uit de eerste bytes van de header die uit 30 bytes bestaat. In de definitie van de ZIP-indeling is opgegeven dat de handtekening voor elke bestandsheader de hexadecimale waarde
0x04034b50
heeft; als de handtekening anders is, betekent dit dat het einde van het bestandsgedeelte van het ZIP-bestand is bereikt en dat er geen bestanden meer zijn om uit te pakken. In dat geval wordt de
while
-lus onmiddellijk beëindigd en wordt niet gewacht tot het einde van de bytearray is bereikt.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
In het volgende deel van de code wordt de headerbyte op positie 8 gelezen en wordt de waarde opgeslagen in de variabele
compMethod
. Deze byte bevat een waarde die aangeeft met welke compressiemethode dit bestand is gecomprimeerd. Er zijn diverse compressiemethoden toegestaan, maar in de praktijk gebruiken bijna alle ZIP-bestanden de Deflate-compressiealgoritme. Als het huidige bestand is gecomprimeerd met Deflate-compressie,
is compMethod
8; als het bestand niet is gecomprimeerd, is
compMethod
0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
Na de eerste 30 bytes volgt een gedeelte van de header met een variabele lengte dat de bestandsnaam en eventueel een extra veld bevat. In de variabele
offset
is de grootte van dit gedeelte opgeslagen. De grootte wordt berekend door de lengte van de bestandsnaam en de lengte van het extra veld op te tellen. Deze waarden worden ingelezen uit de header op positie 26 en 28.
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
Vervolgens wordt uit het gedeelte van de bestandsheader met variabele lengte het aantal bytes ingelezen dat is opgeslagen in de variabele
offset
.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
De bestandsnaam wordt ingelezen vanuit het gedeelte van de header met variabele lengte en samen met de gecomprimeerde en niet-gecomprimeerde (oorspronkelijke) grootte van het bestand weergegeven in het tekstgebied.
// 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';
In het voorbeeld wordt de rest van het bestand vanuit de bestandsstroom ingelezen in
bytes
voor de lengte die is opgegeven door de gecomprimeerde grootte, waarbij de bestandsheader in de eerste 30 bytes wordt overschreven. De gecomprimeerde grootte is nauwkeurig, zelfs als het bestand niet is gecomprimeerd, omdat de gecomprimeerde grootte in dat geval gelijk is aan de niet-gecomprimeerde grootte van het bestand.
// 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);
Vervolgens wordt het gecomprimeerde bestand gedecomprimeerd en wordt de functie
outfile()
opgeroepen om het naar de uitvoerstroom te schrijven. De bestandsnaam en de bytearray die de bestandsgegevens bevat, worden doorgegeven aan
outfile()
.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
In het eerder gegeven voorbeeld werkt
bytes.uncompress(CompressionAlgorithm.DEFLATE)
alleen in AIR-toepassingen. Als u verkleinde gegevens wilt decomprimeren voor zowel AIR als Flash Player, roept u de ByteArray-functie
inflate()
op.
De accolades sluiten geven het einde van de
while
-lus aan en van de
init()
-methode en de Flex-toepassingscode, behalve voor de
outFile()
-methode. De
while
-lus wordt opnieuw gestart en de volgende bytes in het ZIP-bestand worden verwerkt: er wordt een ander bestand uitgepakt of de verwerking van het ZIP-bestand wordt beëindigd als het laatste bestand is verwerkt.
} // end of while loop
} // for Flex version, end of init() method and application
Met de functie
outfile()
wordt in de schrijfmodus op de desktop een uitvoerbestand geopend dat de naam krijgt die is opgegeven met de parameter
filename
. Vervolgens wordt het gegevensbestand vanuit de parameter
data
naar de uitvoerstroom (
outStream
) geschreven en wordt het bestand gesloten.
// 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();
}