Exempel på ByteArray: Läsa en ZIP-fil

Adobe AIR 1.0 och senare

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 bytesLITTLE_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 
    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

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(); 
}