Swift転職なら=>【LevTech】
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大

頭痛が減ったので共有です!

rebuild.fmを応援しています!

HOME > SwiftでZipファイルの解凍や圧縮する方法

SwiftでZipファイルの解凍や圧縮する方法

SwiftでZipの解凍・圧縮処理の解説です。標準ライブラリにZip2があるやら3rdパーティのSSArchiveのObjective-Cのライブラリを Swiftにブリッジして利用するのが良い等の記載がありましたので調査してみます。


参考:zip(), zip3(), unzip(), and unzip3() for Swift
参考:Stupid Swift Ideas
参考:Unzip files in swift
参考:iOSでZIPファイルの圧縮/解凍を簡単に実装する方法

やってみた

ひとまずよくわかっていませんが、最初の記事にある[he Swift Standard Library includes a "Zip2" struct that implements the same thing that my zip() function does, without the function]と 標準ライブラリにもzip関連の関数がありそうなのでチェックしてみます。


参考:Swift Standard Library: Zip2

このサイトにサンプルが置いてあったのでZip2を確認、、、、


Zip2()


// String example
var vs = [ "one", "two", "three", "four", "five", "six" ]
let ps = [ "いち", "に", "さん", "し", "ご", "ろく"]

let pv = map(Zip2(ps, vs)) { "\($0) は英語で \($1)" }
pv

// Numerical example
var v1 = [ 1.0, 2.0, 3.0 ]
var v2 = [ 3.0, 2.0, 1.0 ]

func dot(v1 : [Double], v2: [Double]) -> Double {
    return map(Zip2(v1,v2), *).reduce(0.0, +)
}

dot(v1, v2)

サンプル画像

正直私は下側のNumerical exampleがなぜ10.0なのかよくわかっていませんが、上側のサンプルは配列のそれぞれの要素を一行で 簡単に結合させることができる、とのサンプルだとだけ理解しました。。。つきましては、私がやりたい画像ファイルやテキストファイル一式のZipデータの圧縮・解凍にはZip2は利用できない のかなとの結論です。つきましては、引き続きてSSZipArchiveの検証を行います。


SSZipArchive

Qiita様およびsketchTech様の以下リンクを参考にさせていただいて実行してみます。
参考:iOSでZIPファイルの圧縮/解凍を簡単に実装する方法
参考:Easiest way to zip and unzip files in your iOS app

上記サイトに記載されているようにまずgithubのサイトhttps://github.com/soffes/ssziparchive からプロジェクトをダウンロードします。中に含まれているSSZipArchiveをプロジェクトにそのままドラッグして追加します。

サンプル画像

利用するために、 libz.dylibをTarget->BuildPhaseから追加します。

サンプル画像

サンプル画像

SSZipArchiveはObjective-Cで記載されていますので、Swift側で利用できるようにブリッジさせる為に調整します。Objective-Cとの共存方法は 過去記事:Objective-Cとの共存で解説していますのでよろしければご一読くださいませ。

ここでつまずきました。どうやら共存の方法が少し変わった?Bridge-Header.hが正常に自動作成されなくなった?ようで対応しないといけないようです。調査します。


参考:Swift Bridging Header import issue

解決しました、今までのXcodeでは自動で追加されていましたが手間が増えているようです。AppleはObjective-Cから離れたいのかな。詳細はこちらの記事に追加しておきました

時間がなくなりましたので、圧縮や解凍の処理は明日追記します(2014/2/25)

SSZipArchiveの続き。ー戻ってまいりました。引き続き解凍・圧縮の説明。

▪ 解凍

SSZipArchiveさえ利用できれば処理は簡単なようです


let zipPath = "";//解凍するZIPファイルのパス
let destPath = "";//展開するディレクトリのパス
let ret = SSZipArchive.unzipFileAtPath(zipPath, toDestination: destPath)
if (ret) {
    // 処理が成功した場合
}

※unzipFileAtPath引数をかえればパスワード圧縮されたzipも解凍可能です


    func zipArchiveDidUnzipArchiveAtPath(path: String!, zipInfo: unz_global_info, unzippedPath: String!) {
        println("アンZIP完了!");
    }
    

解凍完了後に処理を行う場合はSSZipArchiveDelegateを親クラスに設定して上記の関数をセットしてください。


▪ 圧縮

引き続きコンプレス、、、圧縮してみます。


let destDirPath = "";//圧縮後のZipファイルの保存ディレクトリパス
let zipFilesArr = [NSBundle.mainBundle().pathForResource("hogeImage", ofType: "jpg"),
    NSBundle.mainBundle().pathForResource("hogeText", ofType: "txt")];
let ret = SSZipArchive.createZipFileAtPath(destDirPath, withFilesAtPaths: zipFilesArr);
if (ret) {
    // 処理が成功した場合
}

ZIP後の保存ディレクトリ先の指定と、圧縮したいファイルを配列に入れてセットします。


let destDirPath = "";//圧縮後のZipファイルの保存ディレクトリパス
let zipDirPath = "";//圧縮したいファイルの保存ディレクトリ
let ret = SSZipArchive.createZipFileAtPath(destDirPath, withContentsOfDirectory: zipDirPath)
if (ret) {
    // 処理が成功した場合
}

引数を変えることでディレクトリ毎圧縮することも可能です


▪ 実際に解凍処理をやってみた

ネットワークからアプリケーション保存領域にサンプルZIPファイルをダウンロードして、 同じディレクトリに解凍する一連を実施してみます

サンプル画像

まずサーバに上記のような画像+テキストファイルが含まれたZIPを作成して以下URLにアップしました。これを解凍していきます。

http://swift-salaryman.com/files/download/ssziparchive_sample.zip

参考:重いファイルのDLをバックグラウンド実行する方法
参考: [swift]HTMLの取得
参考:NSURLSession のまとめ
参考:Download a file with NSURLSession in Swift

これら記事を参考にしつつ、まずZIPをNSURLSessionを利用してダウンロード


import UIKit

class ViewController: UIViewController, NSURLSessionDownloadDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let zipUrlPath = "http://swift-salaryman.com/files/download/ssziparchive_sample.zip"
        
        var sessionConfiguration:NSURLSessionConfiguration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("com.swift-salaryman")
        var session:NSURLSession! = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
        var downloadTask:NSURLSessionDownloadTask = session.downloadTaskWithURL(NSURL(string:zipUrlPath)!);
        downloadTask.resume()
        
    }
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL){
        println(location);
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

このソースでLibraryのキャッシュディレクトリにファイルが格納されていることを確認できました!

NSURLSessionDownloadDelegateではdidFinishDownloadingToURLを実装しなければいけないので注意です。一部サイトではこの関数が少し違っていました。おそらくApple側が protocolの中身を少しずつ変更しているのかもしれません。

printlin(location)で/Library/Developer/CoreSimulator/Devices/ADC8C28B-373E-4356-A407-0D40228ADCFA/data/Containers/Data/Application/BFE374F0-3201-4C76-819E-6E4A34291BA9/Library/Caches/com.apple.nsurlsessiond/Downloads/com.swiftsalaryman.testSSZipArchive/CFNetworkDownload_R6NkPK.tmp みたいなのを吐き出してくれました!

さて、これを解凍するのですが実はSumilatorのデータ保存領域をどうやって確認するのかよくわかっていません。

参考:iOSシミュレータのデータ格納場所
参考:今こそ復習したい、iOSアプリのディレクトリ構成
参考:iOS Fileの置き場所
参考:swiftでiOSからアクセス出来るディレクトリのパス

この記事をみながら、、、と思ったら先ほどの保存格納領域に移動すればよいのですよね、、、とかブツブツローソンの街カフェの隅っこで怪しく自問自答しつつ、、、進めてみます。


import UIKit

class ViewController: UIViewController, NSURLSessionDownloadDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let newsUrlString = "http://swift-salaryman.com/files/download/ssziparchive_sample.zip"
        
        var sessionConfiguration:NSURLSessionConfiguration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("com.company")
        var session:NSURLSession! = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
        var downloadTask:NSURLSessionDownloadTask = session.downloadTaskWithURL(NSURL(string:newsUrlString)!);
        downloadTask.resume()
        
    }
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL){
        println(location);
        unzipSSZipArchiveSample(location.absoluteString!);
    }
    
    func unzipSSZipArchiveSample(location:String){
        
        let DocumentPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
        let zipPath = location;//解凍するZIPファイルのパス
        let destPath = DocumentPath;//展開するディレクトリのパス
        let ret = SSZipArchive.unzipFileAtPath(zipPath, toDestination: destPath)
        if (ret) {
            println("UNZIP処理成功!");
        }
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

これでいけるか、と思いましたがunzipした後の解凍データが存在しない、、、、うーん。なんでだ。UNZIP処理は成功ってログがとれてるんですが、ここで時間切れ。明日また継続します!(2015/2/26)

できました!(2015/2/28)

どうやらSSZipArchiveに渡していたパスがfile://になっていた為でした。最終系のソースを共有します。Swift-salaryman.comからzipをNSURLSessionでダウンロードして アプリケーションのDocumentディレクトリにSSZipArchiveで解凍するサンプルソースです。


import UIKit

class ViewController: UIViewController, NSURLSessionDownloadDelegate, SSZipArchiveDelegate {

    let C_ZIP_TEMP_FILE = "temp.zip";
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let newsUrlString = "http://swift-salaryman.com/files/download/ssziparchive_sample.zip"
        
        //ネットワーク上からZIPをダウンロードする
        var sessionConfiguration:NSURLSessionConfiguration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("com.company")
        var session:NSURLSession! = NSURLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: nil)
        var downloadTask:NSURLSessionDownloadTask = session.downloadTaskWithURL(NSURL(string:newsUrlString)!);
        downloadTask.resume()
        
    }
    
    //ZIPファイルをネットワーク上からDL完了後に呼ばれる
    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL){
        
        let docsDir = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
        let docsDirURL:NSURL? = NSURL.fileURLWithPath(docsDir.stringByAppendingPathComponent(C_ZIP_TEMP_FILE));
        
        //ZIPファイルをCacheディレクトリからDocumentディレクトリへ移動 (tmp -> zip )
        if (NSFileManager.defaultManager().moveItemAtURL(location, toURL: docsDirURL!, error: nil) == true){
            //ZIPファイル解凍
            SSZipArchive.unzipFileAtPath(docsDir+"/"+C_ZIP_TEMP_FILE, toDestination: docsDir, delegate:self);
        };
    }
   
    //ZIPファイル解凍後に呼ばれる
    func zipArchiveDidUnzipArchiveAtPath(path: String!, zipInfo: unz_global_info, unzippedPath: String!) {
        println(unzippedPath + "に解凍しました");
        //ZIPファイル解凍完了後に元データの削除
        let docsDir = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
        NSFileManager.defaultManager().removeItemAtPath(docsDir+"/"+C_ZIP_TEMP_FILE, error: nil);
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

時間がかかってしまいましたが、こちらにて完了です!何かお役にたてれば、、幸いです

まとめ

最後まで記載してから改めて記載します。Objective-Cとの共存の変更やSSZipArchiveに手間取られてしまいました。。。。

↓こんな記事もありますよ!

外部ブラウザでURLを開く

アプリから外部ブラウザ(Safari)でURLを開く方法を記載します。内部的に利用する場合はWebViewを利用します。

SwiftでZipファイルの解凍や圧縮する方法

SwiftでZipの解凍・圧縮処理の解説です。標準ライブラリにZip2があるやら3rdパーティのSSArchiveのObjective-Cのライブラリを Swiftにブリッジして利用するのが良い等の記載がありましたので調査してみます。

willsetとdidsetでプロパティを監視。意味あるのか???

クラスのプロパティにはWillsetとdidsetという監視用の関数があると聞いたのですが、用途は分かるにしても メリットとしてはどういうものなのかなと、うーん、と考えていました。調べていると海外のサイトでは同じように疑問にもつ質問があがっていました。 getとsetで同じことができるのでは?との私と同じスタンスです。解決されていましたので、順に説明していきます。
このエントリーをはてなブックマークに追加
右側のFacebookのLikeをクリック頂けると記事更新の際に通知されますので宜しければご利用下さい!