Cet exemple indique comment lire un fichier .zip simple contenant plusieurs fichiers de types différents. Pour parvenir à lire un tel fichier, les données pertinentes sont extraites des métadonnées pour chacun des fichiers, lesquels sont décompressés dans des classes ByteArray individuelles et écrits dans le poste de travail.
La structure générale d’un fichier .zip repose sur la spécification PKWARE Inc., laquelle est tenue à jour à l’adresse
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
. Elle commence par l’en-tête et les données du premier fichier de l’archive .zip, suivis de la paire en-tête/données de chaque fichier supplémentaire. (La structure de l’en-tête des fichiers est décrite plus loin dans ce document.) Le fichier .zip comprend éventuellement un enregistrement du descripteur de données (généralement lorsque le fichier zip de sortie a été créé dans la mémoire au lieu d’être enregistré sur un disque). Divers éléments facultatifs viennent ensuite : en-tête de déchiffrement de l’archive, enregistrement des données supplémentaires de l’archive, structure de répertoires centrale, fin Zip64 de l’enregistrement de répertoires central, fin Zip64 du localisateur de répertoires central et fin de l’enregistrement de répertoires.
Le code présenté dans cet exemple a été rédigé dans le seul objectif d’analyser des fichiers zip ne contenant pas de dossiers ; il n’attend pas d’enregistrements de descripteurs de données. Il ne tient pas compte des informations suivant les données du dernier fichier.
Le format de l’en-tête de fichier de chaque fichier est défini de la manière suivante :
signature de l’en-tête de fichier
|
4 octets
|
version requise
|
2 octets
|
indicateur de bit à usage général
|
2 octets
|
méthode de compression
|
2 octets (8=DEFLATE; 0=UNCOMPRESSED)
|
heure de la dernière modification du fichier
|
2 octets
|
date de la dernière modification du fichier
|
2 octets
|
crc-32
|
4 octets
|
taille compressée
|
4 octets
|
taille décompressée
|
4 octets
|
longueur du nom de fichier
|
2 octets
|
longueur du champ supplémentaire
|
2 octets
|
nom du fichier
|
variable
|
champ supplémentaire
|
variable
|
L’en-tête du fichier est suivi des véritables données du fichier, au format compressé ou décompressé, suivant l’indicateur de méthode de compression utilisé. L’indicateur est égal à 0 (zéro) si les données du fichier sont décompressées, à 8 si les données sont compressées à l’aide de l’algorithme DEFLATE ou à une autre valeur lorsque les données utilisent d’autres algorithmes de compression.
L’interface utilisateur choisie dans cet exemple se compose d’une étiquette et d’une zone de texte (
taFiles
). L’application écrit les informations suivantes dans la zone de texte pour chaque fichier détecté dans le fichier .zip : nom du fichier, taille compressée et taille décompressée. Le document MXML suivant définit l’interface utilisateur de la version Flex de l’application :
<?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>
Le début du programme effectue les tâches suivantes :
-
Importation des classes requises
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
Définition de l’interface utilisateur pour 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);
-
Définition de la classe ByteArray
bytes
var bytes:ByteArray = new ByteArray();
-
Définition des variables destinées à stocker les métadonnées provenant de l’en-tête du fichier
// 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;
-
Définition des objets File (
zfile
) et FileStream (
zStream
) devant représenter le fichier .zip et indication de l’emplacement du fichier .zip à partir duquel les fichiers sont extraits (fichier intitulé HelloAIR.zip dans le répertoire du poste de travail)
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
Dans Flex, le code du programme commence dans la méthode
init()
, laquelle est appelée en tant que gestionnaire
creationComplete
pour la balise
mx:WindowedApplication
racine.
// for Flex
private function init():void
{
Le programme commence par ouvrir le fichier .zip en mode READ (lecture).
zStream.open(zfile, FileMode.READ);
Il configure ensuite la propriété
endian
de
bytes
sur la valeur
LITTLE_ENDIAN
afin d’indiquer que l’ordre d’octets des champs numériques commence par l’octet le moins important.
bytes.endian = Endian.LITTLE_ENDIAN;
Une instruction
while()
commence ensuite une boucle qui s’interrompt uniquement lorsque la position active dans le flux de fichier est supérieure ou égale à la taille du fichier.
while (zStream.position < zfile.size)
{
La première instruction au sein de la boucle lit les 30 premiers octets du flux de fichier dans la classe ByteArray
bytes
. Les 30 premiers octets constituent la partie à taille fixe de l’en-tête du premier fichier.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
Le code lit ensuite un nombre entier (
signature
) à partir des premiers octets de l’en-tête de 30 octets. La définition du format ZIP indique que la signature de chaque en-tête de fichier correspond à la valeur hexadécimale
0x04034b50
; si la signature est différente, cela signifie que le code a dépassé la section des fichiers contenus dans le fichier .zip et qu’il ne reste plus aucun fichier à extraire. Dans ce cas, le code quitte immédiatement la boucle
while
au lieu d’attendre la fin du tableau d’octets.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
La partie suivante du code lit l’octet d’en-tête à la position décalée 8 et stocke la valeur dans la variable
compMethod
. Cet octet contient une valeur indiquant la méthode de compression appliquée à ce fichier. Plusieurs méthodes de compression sont autorisées, mais en pratique, presque tous les fichiers .zip utilisent l’algorithme de compression DEFLATE. Si le fichier actif est compressé à l’aide de la méthode de compression DEFLATE,
compMethod
est égal à 8 ; si le fichier n’est pas compressé,
compMethod
est égal à 0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
Les 30 premiers octets sont suivis par une partie d’en-tête à longueur variable contenant le nom du fichier et, éventuellement, un champ supplémentaire. La variable
offset
stocke la taille de cette partie. La taille est calculée en ajoutant la longueur du nom de fichier à la longueur du champ supplémentaire, lue à partir de l’en-tête aux décalages 26 et 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
Le programme lit ensuite la partie à longueur variable de l’en-tête du fichier pour identifier le nombre d’octets stockés dans la variable
offset
.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
Le programme lit le nom du fichier à partir de la partie variable de l’en-tête et l’affiche dans la zone de texte accompagné des tailles compressée (zippée) et décompressée (initiale) du fichier.
// 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’exemple de code lit le reste du fichier à partir du flux de fichier en octets (
bytes
) selon la longueur indiquée par la taille compressée, écrasant ainsi l’en-tête de fichier dans les 30 premiers octets. La taille compressée est exacte et ce, même si le fichier n’est pas compressé, car elle équivaut alors à la taille décompressée du fichier.
// 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);
Dans la suite de l’exemple, le fichier compressé est décompressé et la fonction
outfile()
est appelée afin de l’écrire dans le flux du fichier de sortie. Le code passe à
outfile()
le nom du fichier et le tableau d’octets contenant les données du fichier.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
Dans l’exemple précédemment mentionné,
bytes.uncompress(CompressionAlgorithm.DEFLATE)
fonctionne uniquement dans les applications AIR. Pour décompresser les données compressées dans AIR et Flash Player, appelez la fonction
inflate()
de ByteArray.
Les accolades de fermeture indiquent la fin de la boucle
while
, de la méthode
init()
et du code de l’application Flex, à l’exception de la méthode
outFile()
. L’exécution revient au début de la boucle
while
et poursuit le traitement des octets suivants du fichier .zip, soit en extrayant un autre fichier soit en mettant un terme au traitement du fichier .zip si le dernier fichier a été traité.
} // end of while loop
} // for Flex version, end of init() method and application
La fonction
outfile()
ouvre un fichier de sortie en mode WRITE (écriture) dans le poste de travail en lui donnant le nom fourni par le paramètre
filename
. Elle écrit ensuite les données du fichier issues du paramètre
data
dans le flux du fichier de sortie (
outStream
) et ferme le fichier.
// 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();
}