Наверняка многие из Вас уже имели дело со сжатием данных, программируя под 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.
Это те основные моменты, с которыми я хотел Вас, уважаемый читатель, познакомить.
Искренне надеюсь, что эта статья была полезной для Вас.
Успехов!
ссылка на оригинал статьи http://habrahabr.ru/post/196602/
Добавить комментарий