Esse exemplo demonstra como ler um simples arquivo .zip contendo vários arquivos de diferentes tipos. Ele faz isso extraindo dados relevantes dos metadados para cada arquivo, descompactando cada arquivo em um ByteArray e escrevendo o arquivo na área de trabalho.
A estrutura geral de um arquivo .zip é baseada na especificação de PKWARE Inc., mantida em
http://www.pkware.com/documents/casestudies/APPNOTE.TXT
. Primeiramente, está o cabeçalho de um arquivo e os dados do arquivo para o primeiro arquivo no arquivo .zip, seguido por um cabeçalho de arquivo e par de dados de arquivo para cada arquivo adicional. (A estrutura do cabeçalho do arquivo é descrita posteriormente.) Em seguida, o arquivo .zip inclui opcionalmente um registro de descritor de dados (normalmente quando o arquivo zip de saída foi criado na memória, e não salvo em um disco). Em seguida, estão vários elementos opcionais adicionais: cabeçalho de descriptografia do arquivo, registro de dados extra do arquivo, estrutura de diretório central, final Zip64 de registro de diretório central, final Zip64 de localizador de diretório central e final de registro de diretório central.
O código nesse exemplo é escrito para analisar apenas arquivos zip que não contêm pastas e ele não espera registros de descritor de dados. Ele ignora todas as informações seguindo os dados do último arquivo.
O formato do cabeçalho do arquivo para cada arquivo é o seguinte:
assinatura do cabeçalho do arquivo
|
4 bytes
|
versão necessária
|
2 bytes
|
sinalizador de bits de propósito geral
|
2 bytes
|
método de compactação
|
2 bytes (8=DEFLATE; 0=UNCOMPRESSED)
|
última hora de modificação do arquivo
|
2 bytes
|
última data de modificação do arquivo
|
2 bytes
|
crc-32
|
4 bytes
|
tamanho compactado
|
4 bytes
|
tamanho descompactado
|
4 bytes
|
comprimento do nome do arquivo
|
2 bytes
|
comprimento de campo extra
|
2 bytes
|
nome de arquivo
|
variável
|
campo extra
|
variável
|
Seguindo o cabeçalho do arquivo estão os dados reais do arquivo, que podem ser compactados ou não, dependendo do sinalizador de método de compactação. O sinalizador é 0 (zero) se os dados do arquivo são descompactados, 8 se os dados são compactados usando o algoritmo DEFLATE ou outro valor para outros algoritmos de compactação.
A interface de usuário para esse exemplo consiste de um rótulo e uma área de texto (
taFiles
). O aplicativo escreve as seguintes informações na área de texto para cada arquivo encontrado no arquivo .zip: o nome de arquivo, o tamanho compactado e o tamanho descompactado. O documento MXML a seguir define a interface do usuário para a versão Flex do aplicativo:
<?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>
O início do programa executa as seguintes tarefas:
-
Importa as classes necessárias
import flash.filesystem.*;
import flash.utils.ByteArray;
import flash.events.Event;
-
Define a interface de usuário 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 o ByteArray
bytes
var bytes:ByteArray = new ByteArray();
-
Define variáveis para armazenar metadados do cabeçalho do arquivo
// 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 os objetos File (
zfile
) e FileStream (
zStream
) para representar o arquivo .zip e especifica o local do arquivo .zip a partir do qual os arquivos são extraídos — um arquivo chamado “HelloAIR.zip” no diretório da área de trabalho.
// File variables for accessing .zip file
var zfile:File = File.desktopDirectory.resolvePath("HelloAIR.zip");
var zStream:FileStream = new FileStream();
No Flex, o código do programa começa no método
init()
, chamado como manipulador
creationComplete
para a marca raiz
mx:WindowedApplication
.
// for Flex
private function init():void
{
O programa começa abrindo o arquivo .zip em modo READ.
zStream.open(zfile, FileMode.READ);
Ele, em seguida, define a propriedade
endian
de
bytes
para
LITTLE_ENDIAN
para indicar que a ordem de bytes de campos numéricos possui o byte menos significativo primeiro.
bytes.endian = Endian.LITTLE_ENDIAN;
Em seguida, uma instrução
while()
começa um loop que continua até que a posição atual no fluxo do arquivo seja maior que ou igual ao tamanho do arquivo.
while (zStream.position < zfile.size)
{
A primeira instrução dentro do loop lê os primeiros 30 bytes do fluxo do arquivo no ByteArray
bytes
. Os primeiros 30 bytes formam a parte de tamanho fixo do cabeçalho do primeiro arquivo.
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
Em seguida, o código lê um inteiro (
signature
) dos primeiros bytes do cabeçalho de 30 bytes. A definição do formato ZIP especifica que a assinatura para cada cabeçalho de arquivo é o valor hexadecimal
0x04034b50
; se a assinatura for diferente, significa que o código se moveu além da parte do arquivo .zip e não existem mais arquivos para extrair. Nesse caso, o código sai do loop
while
imediatamente, em vez de esperar pelo final da matriz de bytes.
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
A parte seguinte do código lê o byte do cabeçalho na posição de deslocamento 8 e armazena o valor na variável
compMethod
. Esse byte contém um valor que indica o método de compactação usado para compactar esse arquivo. Vários métodos de compactação são permitidos, mas, na prática, praticamente todos os arquivos .zip usam o algoritmo de compactação DEFLATE. Se o arquivo atual é compactado com a compactação DEFLATE,
compMethod
é 8; se o arquivo é descompactado,
compMethod
é 0.
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
Seguindo os primeiros 30 bytes está uma parte de comprimento variável do cabeçalho que contém o nome do arquivo e, possivelmente, um campo extra. A variável
offset
armazena o tamanho dessa parte. O tamanho é calculado pela adição do comprimento do nome do arquivo e o comprimento do campo extra, lido do cabeçalho nos deslocamentos 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
Em seguida, o programa lê a parte de comprimento variável do cabeçalho do arquivo para o número de bytes armazenados na variável
offset
.
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
O programa lê o nome do arquivo da parte de comprimento variável do cabeçalho e o exibe na área de texto, juntamente com os tamanhos compactados (zip) e descompactados (original) do arquivo.
// 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';
O exemplo lê o resto do arquivo do fluxo de arquivos em
bytes
para o comprimento especificado pelo tamanho compactado, substituindo o cabeçalho do arquivo nos primeiros 30 bytes. O tamanho compactado é preciso, mesmo se o arquivo não está compactado, porque, nesse caso, o tamanho compactado é igual ao tamanho descompactado do arquivo.
// 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);
Em seguida, o exemplo descompacta o arquivo compactado e chama a função
outfile()
para escrevê-la no fluxo de arquivos de saída. Ele transmite a
outfile()
o nome do arquivo e a matriz de bytes que contém os dados do arquivo.
if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
No exemplo mencionado anteriormente,
bytes.uncompress(CompressionAlgorithm.DEFLATE)
funcionará somente em aplicativos do AIR. Para expandir os dados não compactados do AIR e do Flash Player, utilize a função de expansão
inflate()
.
As chaves de fechamento indicam o final do loop
while
e do método
init()
e do código do aplicativo Flex, exceto para o método
outFile()
. A execução volta ao início do loop
while
e continua processando os próximos bytes no arquivo .zip — extraindo outro arquivo ou finalizando o processamento do arquivo .zip se o último arquivo tiver sido processado.
} // end of while loop
} // for Flex version, end of init() method and application
A função
outfile()
abre um arquivo de saída no modo WRITE na área de trabalho, dando a ele o nome fornecido pelo parâmetro
filename
. Ela, em seguida, escreve os dados do arquivo do parâmetro
data
para o fluxo do arquivo de saída (
outStream
) e fecha o arquivo.
// 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();
}