Exempel på ByteArray: Läsa en ZIP-filAdobe 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:
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:
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 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(); } |
|