Пример объекта ByteArray: чтение zip-файлаAdobe AIR 1.0 и более новых версий В этом примере показано, как читать простой zip-файл, содержащий несколько файлов разных типов. Для этого информация извлекается из метаданных каждого файла, каждый файл распаковывается в ByteArray и записывается на рабочий стол. Общая структура zip-файла основана на спецификации компании PKWARE, опубликованной по адресу http://www.pkware.com/documents/casestudies/APPNOTE.TXT. Сначала идет заголовок файла и данные первого файла в zip-архиве, затем заголовок и данные второго файла и т. д. (Структура заголовка файла будет рассмотрена немного позднее). Кроме того, zip-файл может содержать запись дескриптора данных (как правило, если итоговый zip-файл был сохранен в память, а не на диск). Также включены несколько дополнительных элементов: заголовок дескриптора архива, запись дополнительных данных архива, структура центрального каталога, конец записи центрального каталога в формате Zip64, локатор центрального каталога в формате Zip64, конец записи центрального каталога. Код в этом примере подходит только для разбора zip-файлов, не содержащих папок, и ему не требуются записи дескрипторов данных. Никакая информация после данных последнего файла в списке не учитывается. Формат заголовка файла для каждого таков:
За заголовком файла следуют собственно данные файла, который может быть в сжатом или несжатом виде, в зависимости от значения флажка метода сжатия. Флажок равен 0 (нулю), если данные не сжаты; 8, если данные сжаты с помощью алгоритма DEFLATE; любое другое значение говорит о том, что для сжатия использовались иные алгоритмы. Пользовательский интерфейс в этом примере состоит из подписи и текстовой области (taFiles). Приложение записывает в текстовую область для каждого файла zip-архива следующую информацию: имя файла, размер после сжатия, размер до сжатия. Следующий документ MXML определяет интерфейс пользователя для версии 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>
В начале программы выполняется ряд задач:
В Flex программный код начинается с метода init(), вызываемого в качестве обработчика события creationComplete для корневого тега mx:WindowedApplication. // for Flex
private function init():void
{
Программа начинается с открытия zip-файла в режиме READ. zStream.open(zfile, FileMode.READ); Затем свойству endian объекта bytes задается значение LITTLE_ENDIAN, чтобы числовые поля читались в прямом порядке байтов. bytes.endian = Endian.LITTLE_ENDIAN; Затем инструкция while() начинает цикл, который заканчивается, когда текущее положение в файловом потоке становится больше или равно размеру файла. while (zStream.position < zfile.size)
{
Первая инструкция в цикле читает первые 30 байт файлового потока в ByteArray bytes. Первые 30 байт составляют часть заголовка файла с фиксированным размером. // read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
Затем код считывает целое число (signature) из первых байтов 30-байтового заголовка. В определении ZIP-формата указано, что подпись каждого заголовка файла содержится в шестнадцатеричном формате 0x04034b50. Если подпись другая, это значит, что код вышел за границы файловой части zip-файла, а файлов для извлечения больше нет. В этом случае цикл while немедленно прекращается, не дожидаясь конца байтового массива. bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
Следующая часть код читает байт заголовка со смещением — из положения 8 и сохраняет значение в переменной compMethod. Этот байт содержит значение, указывающее на метод сжатия, который был применен к файлу. Поддерживается ряд методов сжатия, но на практике почти все zip-файлы сжимаются с помощью алгоритма DEFLATE. Если текущий файл сжат с помощью алгоритма DEFLATE, значение compMethod равно 8; если файл не сжат, значение compMethod равно 0. bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
За первыми 30 байтами следует часть заголовка переменной длины, содержащая имя файла и, возможно, дополнительное поле. Переменная offset хранит размер этой части. Размер подсчитывается путем добавления длины имени файла и длины дополнительного поля, которые читаются из заголовка в положениях 26 и 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
Затем программа читает часть заголовка файла с переменной длиной для установления количества байтов, хранимых в переменной offset. // read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
Программа читает имя файла из части заголовка с переменной длиной и отображает его в текстовой области вместе с данными о размере файла в сжатом и несжатом виде. // 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';
В примере программа читает остаток файла из файлового потока в объект bytes с целью установления длины, заданной в сжатом виде, и переписывает заголовок файла в первых 30 байтах. Размер сжатого файла задается точно, даже если файл не сжат, потому что в таком случае размеры сжатого и несжатого файлов совпадают. // read compressed file to offset 0 of bytes; for uncompressed files
// the compressed and uncompressed size is the same
zStream.readBytes(bytes, 0, compSize);
Затем в примере сжатый файл распаковывается, и вызывается функция outfile(), записывающая его в файловый поток вывода. Функции outfile() передается имя файла и байтовый массив, содержащий данные файла. if (compMethod == 8) // if file is compressed, uncompress
{
bytes.uncompress(CompressionAlgorithm.DEFLATE);
}
outFile(fileName, bytes); // call outFile() to write out the file
Закрывающие скобки указывают на окончание цикла while, метода init() и кода приложения Flex; исключением является метод outFile(). Код возвращается к началу цикла while и обрабатывает следующие байты в zip-файле. При этом либо извлекается следующий файл, либо, если последний файл уже обработан, обработка zip-файла завершается. } // end of while loop } // for Flex version, end of init() method and application Функция outfile() открывает файл вывода на рабочем столе в режиме WRITE и присваивает ему имя, заданное параметром filename. Затем данные файла из параметра data записываются в файловый поток вывода (outStream), а файл закрывается. // 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();
}
|
|