W tym przykładzie przedstawiony jest sposób odczytywania pliku .zip zawierającego kilka plików różnego rodzaju. Następuje to poprzez pobranie istotnych danych z metadanych dla każdego pliku, rozpakowanie każdego pliku do obiektu ByteArray i zapisanie pliku na pulpicie.
Ogólna struktura pliku .zip jest oparta o specyfikację PKWARE Inc. dostępną pod adresem
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
. W pierwszej kolejności w archiwum pliku .zip występuje nagłówek pliku, a następnie nagłówek pliku i pary danych pliku dla każdego dodatkowego pliku. (Struktura nagłówka pliku jest opisana w dalszej części). Następnie plik .zip opcjonalnie zawiera rekord deskryptora danych (zazwyczaj po utworzeniu wyjściowego pliku zip w pamięci, a nie po zapisaniu go na dysk). Dalej znajdują się dodatkowe opcjonalne elementy: nagłówek deskryptora archiwum, rekord dodatkowych danych archiwum, struktura katalogu centralnego, rekord końca katalogu rekord lokalizatora katalogu centralnego Zip64 oraz rekord końca rekordu katalogu centralnego.
W tym przykładzie kod jest zapisany wyłącznie w celu analizy plików zip, które nie zawierają folderów. Kod ten nie oczekuje rekordów deskryptora danych. Ignoruje wszystkie informacje występujące po danych ostatniego pliku.
Format nagłówka pliku dla każdego pliku jest następujący:
podpis nagłówka pliku
|
4 bajty
|
wymagana wersja
|
2 bajty
|
flaga bitowa ogólnego użytku
|
2 bajty
|
metoda kompresji
|
2 bajty (8=DEFLATE; 0=UNCOMPRESSED)
|
godzina ostatniej modyfikacji pliku
|
2 bajty
|
data ostatniej modyfikacji pliku
|
2 bajty
|
crc-32
|
4 bajty
|
skompresowany rozmiar
|
4 bajty
|
rozmiar bez kompresji
|
4 bajty
|
długość nazwy pliku
|
2 bajty
|
długość dodatkowego pola
|
2 bajty
|
nazwa pliku
|
zmienna
|
dodatkowe pole
|
zmienna
|
Właściwe dane pliku znajdują się po nagłówku pliku. Mogą być one skompresowane bądź nie w zależności od flagi metody kompresji. Flaga ma wartość 0 (zero), jeśli dane pliku nie są skompresowane, wartość 8, jeśli dane są skompresowane za pomocą algorytmu DEFLATE lub inną wartość w przypadku innych algorytmów kompresji.
Interfejs użytkownika w tym przykładzie składa się z etykiety i obszaru tekstowego (
taFiles
). Aplikacja zapisuje następujące informacje do obszaru tekstowego dla każdego pliku napotkanego w pliku .zip: nazwę pliku, skompresowany rozmiar i rozmiar bez kompresji. W następującym dokumencie MXML zdefiniowano interfejs użytkownika wersji aplikacji dla środowiska Flex:
<?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>
Na początku programu wykonywane są następujące zadania:
-
Importowanie wymaganych klas
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
Definiowanie interfejsu użytkownika dla programu 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);
-
Definiowanie obiektu ByteArray
bytes
var bytes:ByteArray = new ByteArray();
-
Definiowanie zmiennych w celu zapisania metadanych z nagłówka pliku
// 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;
-
Definiowanie obiektów File (
zfile
) i FileStream (
zStream
) w celu reprezentacji pliku .zip i określenie lokalizacji pliku .zip, z którego rozpakowywane są pliki — plik o nazwie „HelloAIR.zip” w katalogu pulpitu.
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
We Flex, kod programu zostaje uruchomiony w metodzie
init()
wywołanej jako moduł obsługi
creationComplete
dla głównego znacznika
mx:WindowedApplication
.
// for Flex
private function init():void
{
Program rozpoczyna się poprzez otwarcie pliku .zip w trybie odczytu.
zStream.open(zfile, FileMode.READ);
Następnie ustawiona zostaje właściwość
endian
obiektu
bytes
jako
LITTLE_ENDIAN
w celu wskazania, że kolejność bajtów pól numerycznych określa jako pierwsze bajty o najmniejszy znaczeniu.
bytes.endian = Endian.LITTLE_ENDIAN;
Następnie instrukcja
while()
rozpoczyna pętlę ciągnącą się do czasu, aż bieżące położenie w strumieniu plików będzie większe lub równe rozmiarowi pliku.
while (zStream.position < zfile.size)
{
Pierwsza instrukcja w pętli odczytuje pierwsze 30 bajtów strumienia plików do obiektu
bytes
ByteArray. Pierwsze 30 bajtów tworzy pierwszą część nagłówka pliku o stałym rozmiarze.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
Następnie kod odczytuje liczbę całkowitą (
signature
) z pierwszych bajtów 30-bajtowego nagłówka. Definicja formatu ZIP określa, że podpis dla każdego nagłówka pliku to wartość szesnastkowa
0x04034b50
; jeśli podpis ma inną wartość, oznacza to, że kod został przeniesiony poza część pliku .zip i nie ma więcej plików do rozpakowania. W takim wypadku kod wychodzi bezpośrednio poza pętlę
while
i nie czeka na koniec tablicy bajtów.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
Kolejna część kodu odczytuje bajt nagłówka w położeniu przesunięcia 8 i zapisuje wartość w zmiennej
compMethod
. Ten bajt zawiera wartość wskazującą na metodę kompresji użytą do skompresowania tego pliku. Dozwolonych jest kilka metod kompresji, jednak w praktyce prawie wszystkie pliki .zip korzystają z algorytmu kompresji DEFLATE. Jeśli bieżący plik zostanie skompresowany przy użyciu metody DEFLATE, wówczas zmienna
compMethod
będzie miała wartość 8; jeśli plik zostanie zdekompresowany, zmienna
compMethod
będzie miała wartość 0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
Za pierwszymi 30 bajtami znajduje się sekcja nagłówka o zmiennej długości, która zawiera nazwę pliku oraz dodatkowe pole (opcjonalnie). Zmienna
offset
zawiera rozmiar tej sekcji. Rozmiar jest obliczany poprzez dodanie długości nazwy pliku oraz długości pola dodatkowego, które są odczytywane przy przesunięciach 26 i 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
Następnie program odczytuje sekcję nagłówka pliku o zmiennej długości pod względem liczby bajtów zapisanych w zmiennej
offset
.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
Program odczytuje nazwę pliku z sekcji nagłówka o zmiennej długości, a następnie wyświetla nazwę w obszarze tekstowym wraz z rozmiarem pliku skompresowanego (zip) i nieskompresowanego (oryginalnego).
// 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';
W przykładzie przedstawiono odczytanie pozostałej części pliku ze strumienia pliku do
bytes
w celu określenia długości w pliku skompresowanym. W przykładzie następuje również zastąpienie nagłówka pliku w pierwszych 30 bajtach. Rozmiar skompresowany jest określony dokładnie, nawet jeśli plik nie jest skompresowany, ponieważ w tym przypadku wielkość pliku skompresowanego jest równa wielkości pliku nieskompresowanego.
// 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);
Następnie w przykładzie dochodzi do dekompresji skompresowanego pliku oraz do wywołania funkcji
outfile()
w celu zapisania pliku w wyjściowym strumieniu pliku. Do funkcji
outfile()
przekazywana jest nazwa pliku oraz tablica bajtowa zawierająca dane pliku.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
Obiekt
bytes.uncompress(CompressionAlgorithm.DEFLATE)
z poprzedniego przykładu będzie działać wyłącznie w aplikacjach AIR. Aby zdekompresować dla środowiska AIR i programu Flash Player dane, do których zastosowano algorytm deflate, należy wywołać metodę
inflate()
klasy ByteArray.
Nawias zamykający wskazuje koniec pętli
while
, koniec metody
init()
oraz kodu aplikacji Flex — z wyjątkiem metody
outFile()
. Wykonywanie rozpoczyna się od pętli
while
i kontynuuje przetwarzanie kolejnych bajtów w pliku .zip — wyodrębniając inny plik lub kończąc przetwarzanie pliku .zip po przetworzeniu ostatniego pliku.
} // end of while loop
} // for Flex version, end of init() method and application
Funkcja
outfile()
otwiera plik wyjściowy w trybie WRITE na pulpicie, nadając mu nazwę określoną przez parametr
filename
. Następnie zapisuje dane pliku z parametru
data
do wyjściowego strumienia pliku (
outStream
), a następnie zamyka plik.
// 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();
}