Este ejemplo muestra cómo leer un archivo .zip sencillo que contiene varios archivos de diversos tipos. Para realizarlo, se extraen los datos pertinentes de los metadatos para cada archivo, se descomprime cada archivo para ponerlo en un ByteArray y se escribe el archivo en el escritorio.
La estructura general de un archivo .zip se basa en la especificación de PKWARE Inc., que se mantiene en
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
. Primero hay una cabecera de archivo y datos de archivo para el primer archivo individual del archivo .zip, seguido de un par de cabecera-datos de archivo para cada archivo adicional. (La estructura de la cabecera de archivo se describe más adelante). A continuación, el archivo .zip puede incluir como opción un registro de descriptor de datos (generalmente al crear el archivo zip en la memoria, más que al guardarlo en un disco). A continuación vienen varios elementos opcionales más: cabecera de descifrado del archivo comprimido, registro de datos adicionales del archivo comprimido, estructura del directorio central, registro de fin del directorio central Zip64, localizador de fin del directorio central Zip64, y registro de fin del directorio central.
El código en este ejemplo se escribe para que solo se analicen los archivos zip que no contengan carpetas y no espera que haya registros de descripción de datos. Pasa por alto toda la información que haya después del último dato del archivo.
El formato de la cabecera de archivo para cada archivo es el siguiente:
firma de cabecera de archivo
|
4 bytes
|
versión necesaria
|
2 bytes
|
indicador de bits universal
|
2 bytes
|
método de compresión
|
2 bytes (8=DEFLATE; 0=UNCOMPRESSED)
|
hora de última modificación del archivo
|
2 bytes
|
fecha de última modificación del archivo
|
2 bytes
|
crc-32
|
4 bytes
|
tamaño comprimido
|
4 bytes
|
tamaño descomprimido
|
4 bytes
|
longitud de nombre de archivo
|
2 bytes
|
longitud de campo adicional
|
2 bytes
|
nombre de archivo
|
variable
|
campo adicional
|
variable
|
Después de la cabecera del archivo vienen los datos del archivo, que pueden estar comprimidos o sin comprimir, según el indicador de método de compresión. El indicador será 0 (cero) si los datos están sin comprimir, u 8 si los datos están comprimidos con el algoritmo DEFLATE, u otro valor para otros algoritmos de compresión.
La interfaz de usuario para este ejemplo consta de una etiqueta y un área de texto (
taFiles
). La aplicación escribe la información siguiente en el área de texto para cada archivo que encuentra en el archivo .zip: el nombre del archivo, el tamaño comprimido y el tamaño sin comprimir. El siguiente documento MXML define la interfaz de usuario para la versión Flex de la aplicación:
<?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>
El principio del programa realiza las siguientes tareas:
-
Importa las clases que se requieran.
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
Define la interfaz de usuario para 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);
-
Define el ByteArray
bytes
.
var bytes:ByteArray = new ByteArray();
-
Define variables para guardar los metadatos de la cabecera del archivo.
// 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;
-
Define los objetos File (
zfile
) y FileStream (
zStream
) para representar el archivo .zip, y especifica la ubicación del archivo .zip del que se extrajeron los archivos (un archivo llamado “HelloAIR.zip” en el directorio del escritorio).
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
En Flex, el código del programa empieza en el método
init()
, al que se llama como controlador de
creationComplete
para la etiqueta raíz
mx:WindowedApplication
.
// for Flex
private function init():void
{
El programa empieza por abrir el archivo .zip en modo READ (lectura).
zStream.open(zfile, FileMode.READ);
A continuación define la propiedad
endian
de
bytes
en
LITTLE_ENDIAN
para indicar que el orden de bytes de los campos numéricos es con el byte menos significativo primero.
bytes.endian = Endian.LITTLE_ENDIAN;
Seguidamente, la sentencia
while()
inicia un bucle que continúa hasta que la posición actual en la secuencia de archivos sea superior o igual al tamaño del archivo.
while (zStream.position < zfile.size)
{
La primera sentencia del bucle lee los 30 primeros bytes de la secuencia de archivos para ponerlo en el ByteArray
bytes
. Los 30 primeros bytes conforman la parte de tamaño fijo de la cabecera del primer archivo.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
A continuación el código lee un entero (
signature
) en los primeros bytes de la cabecera de 30 bytes. La definición del formato ZIP estipula que la firma de cada cabecera de archivo es el valor hexadecimal
0x04034b50
; si la firma es distinta, significa que el código se refiere a la parte del archivo .zip que es ajena a los archivos y no hay más archivos que extraer. En ese caso el código sale inmediatamente del bucle
while
en lugar de esperar hasta alcanzar el final del conjunto de bytes.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
La siguiente parte del bucle lee el byte de la cabecera en la posición de desplazamiento 8 y guarda el valor en la variable
compMethod
. Este byte contiene un valor que indica el método de compresión que se utilizó para comprimir este archivo. Se admiten varios métodos de compresión, pero en la práctica para casi todos los archivos .zip se utiliza el algoritmo de compresión DEFLATE. Si el archivo actual está comprimido con compresión DEFLATE,
compMethod
es 8; si el archivo está sin comprimir,
compMethod
es 0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
A los 30 primeros bytes sigue una parte de longitud variable de la cabecera que contiene el nombre del archivo y, tal vez, un campo adicional. El tamaño de esta parte se guarda en la variable
offset
. El tamaño se calcula sumando la longitud del nombre del archivo y la longitud del campo adicional, leídas en la cabecera en las posiciones de desplazamiento 26 y 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
A continuación el programa lee la parte de longitud variable de la cabecera de archivo donde se indica la cantidad de bytes guardados en la variable
offset
.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
El programa lee el nombre del archivo en la parte de longitud variable de la cabecera y lo muestra en el área de texto junto con los tamaños del archivo comprimido (en el archivo zip) y sin comprimir (original).
// 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';
En el ejemplo se lee el resto del archivo de la secuencia de archivos para ponerlo en
bytes
durante la longitud especificada por el tamaño comprimido, sobrescribiendo la cabecera del archivo en los primeros 30 bytes. El tamaño comprimido es exacto aunque el archivo esté sin comprimir porque en ese caso el tamaño comprimido es igual al tamaño del archivo sin comprimir.
// 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);
A continuación el código en el ejemplo descomprime el archivo comprimido y llama a la función
outfile()
para escribirlo en la secuencia de archivos de salida. Pasa a
outfile()
el nombre del archivo y el conjunto de bytes que contiene los datos del archivo.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
En el ejemplo mencionado anteriormente,
bytes.uncompress(CompressionAlgorithm.DEFLATE)
solo funcionará en las aplicaciones de AIR. Para no compimir los datos desinflados para AIR y Flash Player, invoque la función
inflate()
de ByteArray.
Las llaves finales indican el final del bucle
while
y del método
init()
y el código de la aplicación de Flex, excepto para el método
outFile()
. La ejecución regresa al principio del bucle
while
y sigue procesando los siguientes bytes del archivo .zip, sea extrayendo otro archivo o finalizando el procesamiento del archivo .zip si es que se ha procesado el último archivo.
} // end of while loop
} // for Flex version, end of init() method and application
La función
outfile()
abre un archivo de salida en modo WRITE (lectura) en el escritorio y le da el nombre suministrado por el parámetro
filename
. A continuación escribe los datos de los archivos del parámetro
data
en la secuencia de archivos de salida (
outStream
) y cierra el archivo.
// 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();
}