...

понедельник, 7 октября 2013 г.

[Из песочницы] Приступая к работе с Objective-Zip

Здравствуйте, уважаемые читатели Хабра!

Наверняка многие из Вас уже имели дело со сжатием данных, программируя под iOS.

Совсем недавно передо мной возникла задача подобного рода. В приложении, над которым я работал, нужно было программно, без потерь данных, сжимать большого объема файлы. Проблема состояла в том, что на устройствах, использующих приложение, не всегда был достаточный объем оперативной памяти. Сжимая огромный файл целиком, приложение просто падало из-за нехватки памяти.

Необходимо было сжимать файл по частям.


Перебрав много разных вариантов, я остановился на очень удобном для подобных задач решении. Этим решением является использование библиотеки Objective-Zip .


Об этой маленькой, но очень удобной и функциональной библиотеке, я и хочу Вам рассказать.


Objective-Zip — это небольшая Objective-C библиотека, которая оборачивает ZLib и MiniZip в единую объектно-ориентированную сущность которая предоставляет основные функции для чтения и записи zip файлов.


Добавление Objective-Zip в Ваш проект




Библиотека распространяется только в виде исходного кода, так что Вам необходимо просто скачать тестовое приложение и скопировать и вставить эти каталоги в собственном проекте:


  • ARCHelper;

  • ZLib;

  • MiniZip;

  • Objective-Zip.


Основные понятия




Библиотека сосредоточена в классе ZipFile. Он может быть создан общей Objective-C процедурой alloc с последующей инициализацией init, с указанием в последнем случае — zip-файл будет создаваться, изменяться или распаковываться:

ZipFile *zipFile= [[ZipFile alloc] initWithFileName:@"test.zip"
mode:ZipFileModeCreate];




Операции создания и добавления имеют модификатор доступа только запись (read-only), в то время как распаковка — только чтение (write-only). Очевидно, что нельзя запрашивать операции создания/добавления при созданном только для распаковки объекте класса и наоборот.

Добавление файла в архив




ZipFile класс имеет несколько методов для добавления новых файлов в zip-архив. Один из методов поддерживает шифрование с помощью пароля, другой шифрование не использует. Оба метода возвращают экземпляр ZipWriteStream класса, который будет использоваться исключительно для записи содержимого файла, а затем должен быть закрыт:

ZipWriteStream *stream= [zipFile writeFileInZipWithName:@"abc.txt"
compressionLevel:ZipCompressionLevelBest];

[stream writeData:abcData];
[stream finishedWriting];


Чтение файла из архива




Для начала мы должны создать объект класса ZipFile, задав ему режим распаковки. Далее мы находим первый файл и от него, до самого конца можем считывать все остальное. Считывание происходит с помощью класса ZipReadStream, который тоже должен быть закрыт после использования:

ZipFile *unzipFile= [[ZipFile alloc] initWithFileName:@"test.zip"
mode:ZipFileModeUnzip];

[unzipFile goToFirstFileInZip];

ZipReadStream *read= [unzipFile readCurrentFileInZip];
NSMutableData *data= [[NSMutableData alloc] initWithLength:256];
int bytesRead= [read readDataWithBuffer:data];

[read finishedReading];




Помните, что в экземпляре NSMutableData, который действует как буфер чтения, поле length должно быть установлено в значение больше, чем 0 (readDataWithBuffer API будет использовать эту длину, чтобы знать сколько байт можно вынести из архива).

Просмотр файлов в архиве




Используя ZipFile класс в режиме распаковки, легко можно получить список файлов архива, заполняя NSArray объектами класса FileInZipInfo. Вы можете использовать его свойство name, чтобы найти файл внутри архива и распаковать его:

ZipFile *unzipFile= [[ZipFile alloc] initWithFileName:@"test.zip"
mode:ZipFileModeUnzip];

NSArray *infos= [unzipFile listFileInZipInfos];
for (FileInZipInfo *info in infos) {
NSLog(@"- %@ %@ %d (%d)", info.name, info.date, info.size,
info.level);

// Locate the file in the zip
[unzipFile locateFileInZip:info.name];

// Expand the file in memory
ZipReadStream *read= [unzipFile readCurrentFileInZip];
NSMutableData *data= [[NSMutableData alloc] initWithLength:256];
int bytesRead= [read readDataWithBuffer:data];
[read finishedReading];
}




Помните, что класс FileInZipInfo предоставляет нам два размера:


  • length — размер оригинального файла (не сжатого);

  • size — размер сжатого файла.


Завершение работы с архивом




После всего, нужно не забывать о закрытии объекта класса ZipFile. Не выполнив нижеуказанную команду, Вы рискуете повредить используемый архив, или вызвать непредвиденное поведение в Вашем проекте.

[zipFile close];


Иерархия файлов/папок внутри архива




Следует отметить, что как таковой иерархии файлов/папок внутри архива нет. Эта иерархия включена в имя файла (например: файл с именем «x/y/z/file.txt»). Это зависит от программы, которая извлекает файл и рассматривает имя как структуру, восстанавливая файл в файловой системе (и наоборот во время создания архива). Общие операции упаковки/распаковки следуют этому правилу.

Управление памятью




Если Вам нужно извлечь огромные файлы, которые не могут содержаться в памяти, Вы можете использовать циклы чтения/записи и буфер, например так:

NSFileHandle *file= [NSFileHandle fileHandleForWritingAtPath:filePath];
NSMutableData *buffer= [[NSMutableData alloc]
initWithLength:BUFFER_SIZE];

ZipReadStream *read= [unzipFile readCurrentFileInZip];

// Read-then-write buffered loop
do {

// Reset buffer length
[buffer setLength:BUFFER_SIZE];

// Expand next chunk of bytes
int bytesRead= [read readDataWithBuffer:buffer];
if (bytesRead > 0) {

// Write what we have read
[buffer setLength:bytesRead];
[file writeData:buffer];

} else
break;

} while (YES);

// Clean up
[file closeFile];
[read finishedReading];
[buffer release];


Обработка исключений




Если вдруг, во время операции, что-то пошло не так — не беспокойтесь. Objective-Zip всегда будет генерировать исключение класса ZipException, который содержит свойство с конкретным кодом ошибки MiniZip. Зная этот код, Вы легко найдете причину ошибки.

Лицензия




Библиотека распространяется под лицензией New BSD License.

P.S.

Это те основные моменты, с которыми я хотел Вас, уважаемый читатель, познакомить.

Искренне надеюсь, что эта статья была полезной для Вас.

Успехов!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:



Комментариев нет:

Отправить комментарий