In questo esempio viene illustrato come leggere un file zip semplice contenente vari file di tipi diversi. A tale scopo, è sufficiente estrarre dati pertinenti dai metadati per ciascun file, decomprimere ciascun file in un ByteArray e scrivere il file sul desktop.
La struttura generale di un file zip si basa sulle specifiche fornite da PKWARE Inc., riportate in
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
. In primo luogo sono presenti un'intestazione file e dati file per il primo file nell'archivio zip, seguiti da una coppia 'intestazione file e dati file per ciascun file aggiuntivo (la struttura dell'intestazione file verrà descritta in seguito). In secondo luogo, il file zip include facoltativamente un record di descrizione dei dati (di solito quando il file zip di output viene creato in memoria anziché salvato sul disco). Sono quindi presenti diversi elementi opzionali aggiuntivi: intestazione di decrittazione dell'archivio, record dei dati extra dell'archivio, struttura di directory centrale, record di fine della directory centrale in formato Zip64, localizzatore di fine della directory centrale in formato Zip64 e record di fine della directory centrale.
Il codice in questo esempio è scritto solo per analizzare i file zip che non contengono cartelle, non prevede record di descrizione dei dati e ignora ogni tipo di informazione che segue gli ultimi dati dei file.
Il formato dell'intestazione per ciascun file è il seguente:
firma dell'intestazione del file
|
4 byte
|
versione richiesta
|
2 byte
|
flag di bit di scopo generale
|
2 byte
|
metodo di compressione
|
2 byte (8=DEFLATE; 0=UNCOMPRESSED)
|
ora dell'ultima modifica del file
|
2 byte
|
data dell'ultima modifica del file
|
2 byte
|
crc-32
|
4 byte
|
dimensione compressa
|
4 byte
|
dimensione non compressa
|
4 byte
|
lunghezza del nome file
|
2 byte
|
lunghezza del campo extra
|
2 byte
|
nome file
|
variabile
|
campo extra
|
variabile
|
L'intestazione del file è seguita dai dati del file veri e propri, che possono essere compressi o non compressi, a seconda del flag del metodo di compressione. Il valore de flag è 0 (zero) se i dati del file non sono compressi, 8 se sono compressi mediante l'algoritmo DEFLATE oppure hanno un valore diverso se è stato usato un altro algoritmo di compressione.
L'interfaccia utente per questo esempio è costituita da un'etichetta e un'area di testo (
taFiles
). L'applicazione scrive le seguenti informazioni nell'area di testo per ciascun file incontrato nel file zip: il nome del file, la dimensione compressa e quella non compressa. Il seguente documento MXML definisce l'interfaccia utente per la versione Flex dell'applicazione:
<?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>
L'inizio del programma effettua le seguenti operazioni:
-
Importa le classi richieste
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
Definisce l'interfaccia utente per 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);
-
Definisce il ByteArray
bytes
var bytes:ByteArray = new ByteArray();
-
Definisce le variabili per archiviare i metadati dall'intestazione del file
// 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;
-
Definisce gli oggetti File (
zfile
) e FileStream (
zStream
) per rappresentare il file zip, quindi specifica la posizione del file zip da cui i file vengono estratti: un file denominato “HelloAIR.zip” nella directory del desktop.
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
In Flex, il codice del programma viene avviato nel metodo
init()
, che viene chiamato come gestore
creationComplete
per il tag
mx:WindowedApplication
dell'elemento principale.
// for Flex
private function init():void
{
Il programma viene avviato aprendo il file zip in modalità di lettura (READ).
zStream.open(zfile, FileMode.READ);
Imposta quindi la proprietà
endian
di
bytes
su
LITTLE_ENDIAN
, a indicare che nell'ordine dei byte dei campi numerici il byte meno significativo è il primo.
bytes.endian = Endian.LITTLE_ENDIAN;
Successivamente, un'istruzione
while()
avvia un ciclo che continua fino a che la posizione corrente nel flusso di file è maggiore o uguale alla dimensione del file.
while (zStream.position < zfile.size)
{
La prima istruzione all'interno del ciclo legge i primi 30 byte del flusso di file nel valore
bytes
di ByteArray. I primi 30 byte costituiscono la parte di dimensione fissa dell'intestazione del primo file.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
In seguito, il codice legge un valore intero (
signature
) dai primi byte dell'intestazione di 30 byte. La definizione del formato ZIP consente di specificare che la firma per ogni intestazione file è costituita dal valore esadecimale
0x04034b50
; se la firma è diversa, ciò significa che il codice è stato spostato al di là della parte file del file zip e che non ci sono altri file da estrarre. In quel caso il codice esce dal ciclo
while
anziché attendere la fine dell'array di byte.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
La parte seguente del codice legge il byte di intestazione alla posizione di offset 8 e memorizza il valore nella variabile
compMethod
. Questo byte contiene un valore che indica il metodo di compressione utilizzato per il file. Sono consentiti diversi metodi di compressione, tuttavia in pratica quasi tutti i file zip utilizzano l'algoritmo di compressione DEFLATE. Se il file corrente è compresso mediante l'algoritmo DEFLATE, il valore di
compMethod
è 8; se il file non è compresso,
compMethod
sarà 0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
Dopo i primi 30 byte è presente una parte dell'intestazione di lunghezza variabile che contiene il nome del file ed eventualmente un campo aggiuntivo. Nella variabile
offset
è memorizzata la dimensione di questa parte. La dimensione viene calcolata aggiungendo la lunghezza del nome file e del campo extra, letta dall'intestazione agli offset 26 e 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
In seguito, il programma legge la parte di lunghezza variabile dell'intestazione del file per il numero di byte archiviati nella variabile
offset
.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
Il programma legge il nome del file dalla parte a lunghezza variabile dell'intestazione e lo visualizza nell'area di testo insieme alle dimensioni del file compresso e non compresso (originale).
// 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';
L'esempio legge il resto del file dal flusso del file in
bytes
per la lunghezza specificata dalla dimensione compressa, sovrascrivendo l'intestazione del file nei primi 30 byte. La dimensione compressa è accurata anche se il file non è compresso, in quanto in quel caso la dimensione compressa del file è uguale a quella non compressa.
// 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);
Successivamente, nell'esempio, il file compresso viene decompresso e viene chiamata la funzione
outfile()
per scriverlo nel flusso di file di output. Vengono quindi trasmessi a
outfile()
il nome del file e l'array di byte contenente i dati del file.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
Nell'esempio citato in precedenza,
bytes.uncompress(CompressionAlgorithm.DEFLATE)
funziona solo nelle applicazioni AIR. Per decomprimere dati compressi in AIR e Flash Player, invocare la funzione
inflate()
di ByteArray.
La parentesi graffa di chiusura indica la fine del ciclo
while
, del metodo
init()
e del codice dell'applicazione, ad eccezione del metodo
outFile()
. L'esecuzione torna all'inizio del ciclo
while
e continua l'elaborazione dei byte seguenti nel file zip, mediante l'estrazione di un altro file oppure terminando l'elaborazione del file zip se l'ultimo file è stato elaborato.
} // end of while loop
} // for Flex version, end of init() method and application
La funzione
outfile()
apre un file di output nella modalità WRITE sul desktop, attribuendo ad esso il nome fornito dal parametro
filename
. I dati del file vengono quindi scritti dal parametro
data
al flusso di file di output (
outStream
) e il file viene chiuso.
// 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();
}