I det här exemplet visas hur en enkel ZIP-fil som innehåller flera filer av olika typ läses. Relevanta data extraheras från metadata för respektive fil, varje fil dekomprimeras till en ByteArray och filen skrivs till skrivbordet.
Den allmänna strukturen för en ZIP-fil baseras på specifikationen från PKWARE Inc., som beskrivs på
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
. Inledningsvis finns ett filhuvud och fildata för den första filen i ZIP-arkivet, följt av ett filhuvud- och fildatapar för varje ytterligare fil. (Filhuvudets struktur beskrivs senare.) Sedan kan ZIP-filen omfatta en databeskrivningspost (vanligtvis när utdata-ZIP-filen skapas i minnet i stället för att sparas på en disk). Därefter finns det flera valfria element: arkivets dekrypteringshuvud, arkivets extra datapost, central katalogstruktur, Zip64-slutpost för central katalog, Zip64-slutlokaliserare för central katalog och slutpost för central katalog.
Koden i det här exemplet skrivs bara för att analysera ZIP-filer som inte innehåller mappar och den förväntar inga databeskrivningsposter. Den ignorerar all information efter den sista fildatan.
Respektive filhuvud har följande format:
filhuvudets signatur
|
4 byte
|
obligatorisk version
|
2 byte
|
allmän bitflagga
|
2 byte
|
komprimeringsmetod
|
2 byte (8=DEKOMPRIMERA; 0=OKOMPRIMERAD)
|
tid för senast ändrade fil
|
2 byte
|
datum för senast ändrade fil
|
2 byte
|
crc-32
|
4 byte
|
komprimerad storlek
|
4 byte
|
okomprimerad storlek
|
4 byte
|
längd på filnamn
|
2 byte
|
längd på extra fält
|
2 byte
|
filnamn
|
variabel
|
extra fält
|
variabel
|
Efter filhuvudet finns den faktiska fildatan, som kan vara komprimerad eller okomprimerad, beroende på komprimeringsmetodflaggan. Flaggan är 0 (noll) om fildatan är okomprimerad och 8 om den har komprimerats med algoritmen DEFLATE eller ett annat värde för andra komprimeringsalgoritmer.
Användargränssnittet för det här exemplet består av en etikett och ett textområde (
taFiles
). Programmet skriver följande information i textområdet för varje fil som påträffas i ZIP-filen: filnamnet, komprimeringsstorleken och den okomprimerade storleken. Följande MXML-dokument definierar användargränssnittet för Flex-versionen av programmet:
<?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>
I början av programmet utförs följande uppgifter:
-
De nödvändiga klasserna importeras
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
Definierar användargränssnittet 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);
-
ByteArrayen
bytes
definieras
var bytes:ByteArray = new ByteArray();
-
Variabler som lagrar metadata från filhuvudet definieras
// 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;
-
Objekten File (
zfile
) och FileStream (
zStream
) definieras till att representera ZIP-filen, och platsen för ZIP-filen som filerna extraheras från definieras – en fil med namnet ”HelloAIR.zip” i skrivbordskatalogen.
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
I Flex startar programkoden i metoden
init()
, som anropas som
creationComplete
-hanterare för rottaggen
mx:WindowedApplication
.
// for Flex
private function init():void
{
Programmet börjar med att öppna ZIP-filen i READ-läge.
zStream.open(zfile, FileMode.READ);
Sedan ställer det in egenskapen
endian
för
bytes
på
LITTLE_ENDIAN
vilket anger att byteordningen för numeriska fält har den minst signifikanta byten först.
bytes.endian = Endian.LITTLE_ENDIAN;
Sedan börjar en
while()
-programsats en slinga som fortsätter tills den aktuella placeringen i filströmmen är större än eller lika med storleken på filen.
while (zStream.position < zfile.size)
{
Den första programsatsen i slingan läser de första 30 byten i filströmmen till ByteArrayen
bytes
. Dessa första 30 byte utgör delen med fast storlek i det första filhuvudet.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
Sedan läser koden ett heltal (
signature
) från några av de första byten i huvudet om 30 byte. ZIP-formatdefinitionen anger att signaturen för varje filhuvud är det hexadecimala värdet
0x04034b50
. Om signaturen är en annan innebär det att koden har flyttats utanför fildelen av ZIP-filen och det finns inga fler filer att extrahera. I så fall avslutar koden
while
-slingan omedelbart i stället för att vänta till slutet på bytearrayen.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
Nästa del av koden läser huvudbyten vid förskjutningsposition 8 och lagrar värdet i variabeln
compMethod
. Denna byte innehåller ett värde som anger vilken komprimeringsmetod som användes för att komprimera filen. Det går att använda olika komprimeringsmetoder, men i praktiken använder nästan alla ZIP-filer komprimeringsalgoritmen DEFLATE. Om den aktuella filen har komprimerats med DEFLATE-komprimering är
compMethod
lika med 8. Om filen är okomprimerad är
compMethod
lika med 0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
Efter dessa första 30 byte finns en del av huvudet med variabel längd som innehåller filnamn och ett extra fält om det är möjligt. Storleken på den här delen lagras i variabeln
offset
. Storleken beräknas genom att längden på filnamnet adderas till längden på det extra fältet, som läses från huvudet vid förskjutningen 26 och 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
Sedan läser programmet delen av filhuvudet med variabel längd och söker efter antalet byte som lagras i variabeln
offset
.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
Programmet läser filnamnet från den variabla delen av huvudet och visar det i textområdet tillsammans med den komprimerade och okomprimerade (ursprungliga) storleken på filen.
// 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';
Exemplet läser resten av filen från filströmmen till
bytes
och söker efter längden som anges av komprimeringsstorleken, och skriver över filhuvudet i de första 30 byten. Den komprimerade storleken är korrekt även om filen inte är komprimerad, eftersom den komprimerade storleken i detta fall är lika med den okomprimerade storleken på filen.
// 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);
Sedan dekomprimerar exemplet den komprimerade filen och anropar funktionen
outfile()
för att skriva den till utdatafilströmmen. Det skickar filnamnet och bytearrayen som innehåller fildata till
outfile()
.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
I det tidigare nämnda exemplet kommer
bytes.uncompress(CompressionAlgorithm.DEFLATE)
endast att fungera i AIR-program. För att få komprimerade data okomprimerade för både AIR och Flash Player, anropar du
inflate()
-funktionen i ByteArray.
De avslutande klammerparenteserna anger slutet på
while
-slingan, och för
init()
-metoden och Flex-programkoden, med undantag för metoden
outFile()
. Körningen gör en slinga tillbaka till början av
while
-slingan och fortsätter att bearbeta nästa byte i ZIP-filen, antingen med att extrahera en annan fil eller avsluta bearbetningen av ZIP-filen om den sista filen har bearbetats.
} // end of while loop
} // for Flex version, end of init() method and application
Funktionen
outfile()
öppnar en utdatafil i WRITE-läge på skrivbordet, och ger den namnet som anges i parametern
filename
. Sedan skrivs fildata från
data
-parametern till utdatafilströmmen (
outStream
) och filen stängs.
// 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();
}