LoginSignup
1
1

More than 5 years have passed since last update.

Objective-C-ZIPライブラリを用いて、フォルダ構造を保ったままzip化を行う

Last updated at Posted at 2019-02-22

Objective-C-ZIPライブラリを用いて、フォルダ構造を保ったままzip化を行う

背景

  • POSIX関数のzip -rj ...でやろうとしたが、フォルダが含まれなくなるため不可。(全てのファイルがルートフォルダに設置される。)
    • ターミナル同様にcdを行って…とできれば実現できるのだけど。
  • zipでは限界がある(フォルダ構造が保てない等)ので、Objective-Zipライブラリを用いる必要に駆られた。
  • Objective-Zipという外部ライブラリを用いる。

期待される出力結果

  • 下記ファイル構造を保ったままzip化を行う。
  • 下記フォルダをダウンロードフォルダ直下に配置

image.png

  • 作成したzipファイルと解凍結果

image.png

CocoaPodsでのライブラリ追加

下記を参考に導入。

https://qiita.com/pekocalypse/items/b80f6c343355a872dde6
https://qiita.com/satoken0417/items/479bcdf91cff2634ffb1

Podfileは下記の通り。
platform :osx, '10.13.0'の指定でいいかは自信がない。

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'
platform :osx, '10.13.0'
pod 'objective-zip', '~> 1.0'

target 'ObjectiveC_ZIP_LIB_TEST' do
  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
  # use_frameworks!

  # Pods for ObjectiveC_ZIP_LIB_TEST

end

GithubのREADMEの補足

Objective-Zipの使い方に関しては、基本READMEを参照する。
ただしGitHubの説明通りにzipファイルを作成すると解凍でエラーが発生する。(下記コードで行うとエラーが発生する)

    OZZipFile* zip = [[OZZipFile alloc] initWithFileName:@"test.zip" mode:OZZipFileModeCreate];
    OZZipWriteStream* write = [zip writeFileInZipWithName:@"hello.txt"
                                       compressionLevel:OZZipCompressionLevelBest];
    NSData* data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
    [write writeData:data];
    [write finishedWriting];
    [zip close];

image.png

64bitモードに問題があるそうです。

You are having trouble with the 64 bit mode that objective-zip can use.

If you just add legacy32BitMode:YES when creating the archive, everything will be fine.

32bitモードを使用すると良いそうです。

OZZipFile *zipFile= [[OZZipFile alloc] initWithFileName:zipPath 
                                                   mode:OZZipFileModeCreate 
                                        legacy32BitMode:YES];

zip内のフォルダ構造を維持するには

フォルダ構造は各ファイルをzip化するときにパス指定で行うことで作成する。

File/folder hierarchy inide the zip
Please note that inside the zip files there is no representation of a file-folder hierarchy: it is simply embedded in file names (i.e.: a file with a name like "x/y/z/file.txt"). It is up to the program that extracts files to consider these file names as expressing a structure and rebuild it on the file system (and viceversa during creation). Common zippers/unzippers simply follow this rule.

例:relativeFilePath@"x/y/z/file.txt"を指定する。

OZZipWriteStream *zipWriteStream = [zip writeFileInZipWithName:relativeFilePath
                                                      compressionLevel:OZZipCompressionLevelBest];

作成したプログラム

冒頭部

#import "AppDelegate.h"
#import "Objective-Zip.h"
static NSString *const kTargetFolderPath = @"~/Downloads/SampleFolder"; // zip化対象フォルダ
static NSString *const kZipName          = @"result.zip";               // zip化する際のファイル名

呼び出し部

NSString *targetFolderPath = [kTargetFolderPath stringByExpandingTildeInPath];
[self zipFilesWithFolderURL:[NSURL fileURLWithPath:targetFolderPath] zipName:kZipName];

関数部

/**
 @brief フォルダ構造を保ったままフォルダ内のファイルのzip化を行う
 @param dirURL zip化対象フォルダ
 @param aZipName 作成するzip名
 */
- (void)zipFilesWithFolderURL:(NSURL *)dirURL zipName:(NSString *)aZipName {
    NSArray   *fileURLList = [self getFileURLListWithFolderURL:dirURL];
    NSString  *zipPath     = [NSBundle.mainBundle.bundlePath.stringByDeletingLastPathComponent stringByAppendingPathComponent:aZipName];
    OZZipFile *zip         = [[OZZipFile alloc] initWithFileName:zipPath
                                                            mode:OZZipFileModeCreate
                                                 legacy32BitMode:YES];
    for (NSURL *fileURL in fileURLList) {
        // zip内の構造を示すための、相対パスを作成する
        NSMutableString *relativeFilePath = [[NSMutableString alloc] initWithString:fileURL.path];
        NSRange range = [relativeFilePath rangeOfString:dirURL.path];
        [relativeFilePath deleteCharactersInRange:range]; // 相対パス部分のみ残す

        OZZipWriteStream *zipWriteStream = [zip writeFileInZipWithName:relativeFilePath
                                                      compressionLevel:OZZipCompressionLevelBest];
        NSData* fileData = [NSData dataWithContentsOfURL:fileURL];
        [zipWriteStream writeData:fileData];
        [zipWriteStream finishedWriting];
    }
    [zip close];
    return;
}

/**
 @brief 対象フォルダ内の全ファイルのURL配列を取得する
 @param aDirectory 検索対象フォルダ
 @return ファイルのURL配列
 @warning pkg及び隠しファイル及びサブフォルダ自体を無視
 */
- (NSArray *)getFileURLListWithFolderURL:(NSURL *)aDirectory {
    NSMutableArray *fileURLList = [NSMutableArray array];
    // フォルダ内のファイルのパスを順番に取得する
    NSFileManager *fileManager = NSFileManager.defaultManager;
    // ディレクトリ用の列挙子を取得する(pkg及び隠しファイルは除外する)
    NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtURL:aDirectory
                                          includingPropertiesForKeys:nil
                                                             options:NSDirectoryEnumerationSkipsPackageDescendants | NSDirectoryEnumerationSkipsHiddenFiles
                                                        errorHandler:nil];
    for (NSURL *subURL in enumerator) {
        BOOL isDir = NO;
        [fileManager fileExistsAtPath:subURL.path isDirectory:&isDir];
        if (isDir) {
            continue;   // フォルダは対象外
        }
        [fileURLList addObject:subURL];
    }
    return fileURLList.copy;
}
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1