Создание ZIP-архива из приложения Какао

Существуют ли классы Objective-C, эквивалентные классам, содержащимся в пакете Java java.util.zip?
Является ли выполнение команды CLI единственной альтернативой?


person apaderno    schedule 18.12.2009    source источник


Ответы (7)


Начиная с iOS8 / OSX10.10 существует встроенный способ создания zip-архивов с использованием NSFileCoordinatorReadingOptions.ForUploading. Простой пример создания zip-архивов без каких-либо зависимостей, отличных от Cocoa:

public extension NSURL {

    /// Creates a zip archive of the file/folder represented by this URL and returns a references to the zipped file
    ///
    /// - parameter dest: the destination URL; if nil, the destination will be this URL with ".zip" appended
    func zip(dest: NSURL? = nil) throws -> NSURL {
        let destURL = dest ?? self.URLByAppendingPathExtension("zip")

        let fm = NSFileManager.defaultManager()
        var isDir: ObjCBool = false

        let srcDir: NSURL
        let srcDirIsTemporary: Bool
        if let path = self.path where self.fileURL && fm.fileExistsAtPath(path, isDirectory: &isDir) && isDir.boolValue == true {
            // this URL is a directory: just zip it in-place
            srcDir = self
            srcDirIsTemporary = false
        } else {
            // otherwise we need to copy the simple file to a temporary directory in order for
            // NSFileCoordinatorReadingOptions.ForUploading to actually zip it up
            srcDir = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(NSUUID().UUIDString)
            try fm.createDirectoryAtURL(srcDir, withIntermediateDirectories: true, attributes: nil)
            let tmpURL = srcDir.URLByAppendingPathComponent(self.lastPathComponent ?? "file")
            try fm.copyItemAtURL(self, toURL: tmpURL)
            srcDirIsTemporary = true
        }

        let coord = NSFileCoordinator()
        var error: NSError?

        // coordinateReadingItemAtURL is invoked synchronously, but the passed in zippedURL is only valid 
        // for the duration of the block, so it needs to be copied out
        coord.coordinateReadingItemAtURL(srcDir, options: NSFileCoordinatorReadingOptions.ForUploading, error: &error) { (zippedURL: NSURL) -> Void in
            do {
                try fm.copyItemAtURL(zippedURL, toURL: destURL)
            } catch let err {
                error = err as NSError
            }
        }

        if srcDirIsTemporary { try fm.removeItemAtURL(srcDir) }
        if let error = error { throw error }
        return destURL
    }
}

public extension NSData {
    /// Creates a zip archive of this data via a temporary file and returns the zipped contents
    func zip() throws -> NSData {
        let tmpURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent(NSUUID().UUIDString)
        try self.writeToURL(tmpURL, options: NSDataWritingOptions.DataWritingAtomic)
        let zipURL = try tmpURL.zip()
        let fm = NSFileManager.defaultManager()
        let zippedData = try NSData(contentsOfURL: zipURL, options: NSDataReadingOptions())
        try fm.removeItemAtURL(tmpURL) // clean up
        try fm.removeItemAtURL(zipURL)
        return zippedData
    }
}
person marcprux    schedule 22.09.2015
comment
Это здорово, но что насчет обратного? То есть иметь zip-файл и затем читать его содержимое, как если бы это был каталог? - person adib; 31.12.2015
comment
Я посмотрел, но не смог найти подобного метода, чтобы пойти другим путем. Я проголосовал за этот ответ, потому что он потрясающий. Однако в моем приложении для Mac я вместо этого использовал NSTask для вызова / usr / bin / zip и / usr / bin / unzip. Он прост, предлагает множество задокументированных параметров для управления поведением, и в моем случае потребовалось меньше кода, чем этот. - person Jerry Krinock; 26.02.2016
comment
Почему этот подход не получил широкого распространения? Идеальное встроенное решение без какой-либо дополнительной библиотеки, которую вам нужно поддерживать. Перевел его в цель c, и, похоже, он тоже работает. - person Bruno Bieri; 17.12.2020

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

person Peter Hosey    schedule 18.12.2009
comment
Использование этого метода позволит не изменять код для поддержки новых функций. Интересно, что именно делает Finder, когда вы выбираете файл / каталог, а затем выбираете в меню «Сжать». Какой исполняемый файл использует Finder? - person apaderno; 19.12.2009

person miku    schedule 18.12.2009
comment
ZipKit, кажется, лучше называет свои методы, чем ZipArchive (даже если я не понимаю, почему он ставит префиксы для своих методов добавления). - person apaderno; 18.12.2009
comment
Методы категорий с префиксом или суффиксом помогают предотвратить конфликты имен, если Apple когда-либо добавит метод в Какао под тем же именем. - person Peter Hosey; 19.12.2009
comment
ZipKit теперь находится на github.com/kolpanic/ZipKit и больше не на битбакете (шокер) - person uchuugaka; 17.10.2013

Ознакомьтесь с http://code.google.com/p/ziparchive/. Это класс для архивирования файлов. Google - ваш друг!

person Ben Gottlieb    schedule 18.12.2009

Переведен ответ @marcprux на Objective-C. Пожалуйста, укажите его ответ, если он вам пригодится:

NSURL + Compression.h

#import <Foundation/Foundation.h>


@interface NSURL (NSURLExtension)

- (NSURL*)zip;

@end

NSURL + Compression.m

#import "NSURL+Compression.h"


@implementation NSURL (NSURLExtension)


-(NSURL*)zip
{
  BOOL   isDirectory;
  BOOL   hasTempDirectory = FALSE;
  NSURL* sourceURL;

  NSFileManager* fileManager = [NSFileManager defaultManager];
  BOOL           fileExists  = [fileManager fileExistsAtPath:self.path isDirectory:&isDirectory];

  NSURL* destinationURL = [self URLByAppendingPathExtension:@"zip"];

  if(fileExists && isDirectory)
  {
    sourceURL = self;
  }

  else
  {
    sourceURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
    [fileManager createDirectoryAtURL:sourceURL withIntermediateDirectories:TRUE attributes:nil error:nil];

    NSString* pathComponent = self.lastPathComponent ? self.lastPathComponent : @"file";
    [fileManager copyItemAtURL:self toURL:[sourceURL URLByAppendingPathComponent:pathComponent] error:nil];

    hasTempDirectory = TRUE;
  }

  NSFileCoordinator* fileCoordinator = [[NSFileCoordinator alloc] init];

  [fileCoordinator coordinateReadingItemAtURL:sourceURL options:NSFileCoordinatorReadingForUploading error:nil byAccessor:^(NSURL* zippedURL)
   {
    [fileManager copyItemAtURL:zippedURL toURL:destinationURL error:nil];
   }];

  if(hasTempDirectory)
  {
    [fileManager removeItemAtURL:sourceURL error:nil];
  }

  return destinationURL;
}


@end

NSData + Compression.h

#import <Foundation/Foundation.h>


@interface NSData (NSDataExtension)

- (NSData*)zip;

@end

NSData + Compression.m

#import "NSData+Compression.h"
#import "NSURL+Compression.h"


@implementation NSData (NSDataExtension)


// Creates a zip archive of this data via a temporary file and returns the zipped contents
// Swift to objective c from https://stackoverflow.com/a/32723162/
-(NSData*)zip
{
  NSURL* temporaryURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:[[NSUUID UUID] UUIDString]];
  [self writeToURL:temporaryURL options:NSDataWritingAtomic error:nil];
  NSURL* zipURL = [temporaryURL zip];

  NSFileManager* fileManager = [NSFileManager defaultManager];
  NSData*        zippedData  = [NSData dataWithContentsOfURL:zipURL options:NSDataReadingMapped error:nil];

  [fileManager removeItemAtURL:temporaryURL error:nil];
  [fileManager removeItemAtURL:zipURL error:nil];
  
  return zippedData;
}


@end

Пожалуйста, дайте мне знать о любых улучшениях.

person Bruno Bieri    schedule 17.12.2020

Ознакомьтесь с zipzap, моей быстрой библиотекой ввода-вывода для zip-файлов.

person Glen Low    schedule 10.09.2014

Также: http://www.cocoadev.com/index.pl?NSDataCategory.

person djromero    schedule 10.06.2011