JScriptでZIP圧縮＆解凍（MS非推奨）

## はじめに

「Scripting.FileSystemObject」と「Shell.Application」を利用して、ZIPファイルの圧縮や解凍をやってみます。
「CopyHere」メソッドによるZIP処理はエラー処理が不完全な事を理由に、
**Microsoftでは「サポート対象外」となっています**ので、確実にやりたい場合は、
コマンドラインの圧縮モジュールなどを使ったほうが良いかもしれません。

ファイルオブジェクトとシェルオブジェクトを定義します。

```js
// ファイルオブジェクト取得
var fso = new ActiveXObject("Scripting.FileSystemObject");
// シェル取得
var shell = new ActiveXObject("Shell.Application");
```


## 圧縮

圧縮は以下の手順で行います。

1. 空のZIPファイルを作成する。
2. 空のZIPファイルにファイルをコピーする。
3. ZIP圧縮中は読み書き禁止なので、読み書き出来るまで待機する。

空のZIPファイルは「End of central directory record」というZIPファイル情報を
ファイル0個ととして、新規ファイルに書き込みます。
バイナリーコードですので、「fromCharCode」メソッドを利用していったん文字列として書き込んでいます。

また、読み書き出来るまで待機する方法として、読み書き可能としてファイルを開き
エラーが出るか、出ないかで判断しています。

サンプルコードでは、
第１引数に、ZIPファイル
第２引数に、ファイル、フォルダ、または、ファイルとフォルダのリスト
を指定するようになっています。

```js
function zip(zipfile, files) {
    // ZIPに格納するアイテム
    var items = [];
    // ファイル存在チェック
    if (files) {
        // ファイル配列チェック
        if (files instanceof Array) {
            // 配列の場合はアイテムに設定
            items = files;
        } else {
            // 配列ではないので、配列に追加
            items.push(files);
        }
    }
    // ZIPファイル存在チェック
    if (!fso.FileExists(zipfile) && !fso.FolderExists(zipfile)) {
        var ts = null;
        try {
            // ZIPファイル生成
            ts = fso.OpenTextFile(zipfile, 2, true);
            // 空のZIPファイル生成
            ts.Write(String.fromCharCode(0x50, 0x4B, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
        } catch (e) {
            // エラーの場合
            throw e;
        } finally {
            try {
                if (ts != null) {
                    // ファイル閉じる
                    ts.Close();
                }
            } catch (e) {
                // エラーの場合
                throw e;
            }
        }
    }
    // 特殊フォルダ取得（ZIP）
    var zip = shell.NameSpace(zipfile);
    // アイテム数分ループ
    for (var i = 0; i < items.length; i++) {
        // ファイル又はフォルダ存在チェック
        if (fso.FileExists(items[i]) || fso.FolderExists(items[i])) {
            // ZIPファイルにコピー
            zip.CopyHere(items[i], 4);
            while (true) {
                // 待機
                WScript.Sleep(100);
                try {
                    // ZIPファイルが読書可能かチェック
                    fso.OpenTextFile(zipfile, 8, false).Close();
                    // 可能な場合は終了
                    break;
                } catch (e) {
                    // 不可能な場合は再度ループ
                }
            }
        }
    }
}
```

一応、「CopyHere」のオプション（第２引数）に、プログレスバー非表示(4)を設定しているんですが、
効いてないようです。

また、ZIPファイルの待機方法として、ZIPファイル内のファイル数で判定する方法もあるようです。

## 解凍

ZIPファイル内のファイルアイテムをフォルダにコピーします。

フォルダ生成には、前回作成した「[createFolder](http://qiita.com/tnakagawa/items/c9802f4a54239b703336)」メソッドを利用しています。

こちらは、処理の待ち合わせが不要なようです・・・

```js
function unzip(zipfile, folder) {
    // ZIPファイル存在＆拡張子判定
    if (fso.FileExists(zipfile)
        && fso.GetExtensionName(zipfile).toLowerCase() == "zip") {
        var unzip = null;
        // パラメータチェック
        if (folder) {
            // フォルダ存在チェック
            if (!fso.FolderExists(folder)) {
                // フォルダが無い場合、生成
                createFolder(folder);
            }
            // ZIPファイル名設定
            unzip = folder;
        }
        // ZIPファイル存在チェック
        if (unzip != null) {
            // ZIP解凍
            shell.NameSpace(folder).CopyHere(shell.NameSpace(zipfile).Items(), 20);
        }
    }
}
```

## コマンドラインを利用した圧縮＆解凍

ここで記述すべき事ではないかもしれませんが、一応「7-Zip」のコマンドライン版にて、圧縮＆解凍してみます。
実行するjsと同じフォルダに、「7za.exe」と圧縮対象のファイル「log.txt」があるこを条件とします。

事前に以下が定義されているとします。

```js
// ファイルオブジェクト取得
var fso = new ActiveXObject("Scripting.FileSystemObject");
// Scriptのカレントパス取得
var folderName = fso.GetParentFolderName(WScript.ScriptFullName);
// 「WScript.Shell」生成
var wshell = new ActiveXObject("WScript.Shell");
```

* 圧縮

```js
// コマンド生成
var cmd = "\"" + folderName + "\\7za.exe\""   // 「7za.exe」パス
        + " a"                                // 追加
        + " \"" + folderName + "\\log.zip\""  // 圧縮ファイル
        + " \"" + folderName + "\\log.txt\""; // 圧縮対象ファイル
// コマンド実行
var exec = wshell.Exec(cmd);
// コマンド終了まで待機
while (exec.Status == 0) {
    // 待機
    WScript.Sleep(100);
}
```

* 解凍

```js
// コマンド生成
var cmd = "\"" + folderName + "\\7za.exe\""  // 「7za.exe」パス
        + " x"                               // 解凍
        + " \"" + folderName + "\\log.zip\"" // 圧縮ファイル
        + " -o\"" + folderName + "\\log\"";  // 解凍先
// コマンド実行
var exec = wshell.Exec(cmd);
// コマンド終了まで待機
while (exec.Status == 0) {
    // 待機
    WScript.Sleep(100);
}
```

コマンドラインですので、ここでは「7-Zip」版を使いましたがなんでも構いません。