VBScriptでファイルをZIP圧縮する方法
前説
前回の続き、今回はVBScriptでファイルをZIP圧縮する方法を紹介したいと思います。
ファイルをZIP圧縮することは、実際のプロジェクトにおいて、バッチのログファイルの圧縮処理でよく使われていると思います。VBScriptでファイルをZIP圧縮する方法は主に2つがあります。
一つ目は第三者ソフトウェアを利用してVBScriptでファイルをZIP圧縮することです。
メリット:実装はめちゃくちゃ簡単!ソースを読み易いし、使い勝手がめちゃくちゃ良いし、パスワード付きや分割アーカイブなどWindowsがサポートしない機能を提供するので、お勧めです。
デメリット:第三者ソフトウェアの導入、構築手順書作成が必要となります。また、恐らくないと思いますが、お客様の環境で他のソフトウェアと競合し予想外のトラブルが発生した場合があります。
二つ目はWindowsに標準で搭載される機能を利用してVBScriptでファイルをZIP圧縮することです。
メリット:Windowsに標準で搭載されるので、第三者ソフトウェアの導入、構築手順書作成は不要、お客様の環境でソフトウェアの導入が制限され、また導入不可の場合、これは唯一の選択肢だと思います。
デメリット:実装が面倒くさい!特に複数、サイズが大きなファイルを圧縮する場合、実装によって、たまにうまく圧縮できないケースがあり、運用段階に原因不明の不具合が起きる恐れがあります。また、パスワード付き機能が必須の場合、選択肢から外されます。
ということで、第三者ソフトウェアを利用してVBScriptでファイルをZIP圧縮することは一番お勧めですが、第三者ソフトウェアによって実装方法が変わるため、紹介を割愛させていただきます。winzipや7-zipなど素晴らしいソフトウェアがいっぱいありますので、ネットで調べたら実装方法がわかるかと思います。
それでは、今回はWindowsに標準で搭載される機能でVBScriptでファイルをZIP圧縮する方法を紹介したいと思います。
ZIPとは
筆者もそうなんですが、ZIPの本質を知らない方が意外と多いです。ZIPは一体何でしょうか。
ZIPはファイルなんです!ZIPはPDFなどと同じ、独自のフォーマットを持つファイルなんです!普段はZIPファイルと呼んでいるのに、フォルダの感覚で操作できるため、Windowsユーザーがフォルダとよく勘違いしているかと思います。ZIPはフォルダではなく、ファイルなんです!ちなみに、ZIPのマジックナンバー (フォーマット識別子)は「PK\003\004」です。詳しい情報はウィキペディアで調べてください。
一つのファイルを圧縮する方法
まずは、簡単なVBScriptでファイルをZIP圧縮する方法からを紹介したいと思います。考え方は、
1.ZIPファイルを作成する
2.対象ファイルをZIPファイルにコピーする
ファイルにファイルをコピーすることが不思議だと思いますが、Windowsの内部では、ZIPファイルを(圧縮)フォルダーとして取り扱うことが事前に分かれば、ストレスなしで理解できるかと思います。(筆者の場合、当初、ここで凄く悩んでいました。)
早速、サンプルを提供します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
Dim objFileSys, objShell targetFile = "C:\batch\test.txt" zipFile = "C:\batch\zip_sample\test.zip" '①FileSystemObjectを作成する Set objFileSys = CreateObject("Scripting.FileSystemObject") '②ZIPファイルを作成する With objFileSys.CreateTextFile(zipFile , True) .Write "PK" & Chr(5) & Chr(6) & String(18,0) .Close End With '③Applicationを作成する Set objShell = CreateObject("Shell.Application") '④対象ファイルをZIPファイルにコピーする objShell.NameSpace(zipFile).CopyHere(targetFile) '⑤下記の実装がない場合と、ZIP圧縮することが成立できない numZip = 0 Do While numZip = 0 WScript.Sleep(250) numZip = objShell.NameSpace(zipFile).Items.count Loop Set objFileSys = Nothing Set objShell = Nothing |
ソースを少し展開し説明します。
まずは、①FileSystemObjectから②ZIPファイルを作成し、前述のようにマジックナンバーをファイルの先頭に付けます。
次は、WindowsがZIPファイルを(圧縮)フォルダーとして取り扱うため、③ApplicationのNameSpaceメソッドからZIP特殊フォルダオブジェクトを取得し、④対象ファイルをZIPファイル(圧縮フォルダ)にコピーします。
普通に考えると、ここまで実装完成だと思いますが、実際に落とし穴があります。それは、ZIP特殊フォルダオブジェクトのCopyHereメソッドの処理が非同期なんです!つまり、待たずにファイルコピーの途中にプログラムを終了する場合、ファイルコピーのプロセスの帰り道がなくなり、結果、ZIPファイルの中に何もなくて空のままでした。
この非同期処理は後ほどに説明するように、不具合のモトになるので、第三者ソフトウェアを利用する方法をお勧めする最大理由です。
この非同期処理を救う方法は、注釈⑤の通りで、ファイルの数をカウントし、数がゼロではないまでず~と待っているということです。また、待つ(スリープ)時間は適当に設定すれば良いかと思います。
複数ファイルを圧縮する方法
複数ファイルの場合、基本的に一つのファイルの繰り返しですが、前述の通り、CopyHereメソッドの処理が非同期のため、スリープ時間が必要となります。2,3個サイズが小さいファイルであれば、特に問題ないかと思いますが、実際に(100単位、1万単位)大量なファイルを圧縮する場合、ファイルを一つずつコピーし、そのたびにスリープ時間が掛かるため、全体処理時間が掛かるし、ファイルの種類やサイズによって、スリープ時間を設けても、ごく一部ファイルを圧縮し、ほかはうまく圧縮できなかったという不思議な不具合が筆者のプロジェクトで実際に発生してしまったため、ファイル単位で繰り返し圧縮する方法はお勧めしません。
じゃ、どうすればいいのでしょうか。答えは、次に紹介します。
フォルダを圧縮する方法
フォルダを圧縮する方法は基本、一つのファイルを圧縮する方法と同じです。ファイル名をフォルダ名に書き換えすればOKです。フォルダを圧縮する時に、フォルダの中にあるファイルやサブフォルダを纏めて一つの塊として処理するため、上記の複数ファイルを圧縮する場合、対象ファイルを一つのフォルダに集めて、纏めて圧縮するなら、不思議な不具合を自然に解決できます。また、Windowsは空フォルダを圧縮することができないため、同じな発想で、別のフォルダに入れてから圧縮すれば解決できます。
一応、サンプルも提供します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
Dim objFileSys, objShell targetFolder = "C:\batch\multi_file" zipFile = "C:\batch\zip_sample\test.zip" Set objFileSys = CreateObject("Scripting.FileSystemObject") 'ZIPファイルを作成する With objFileSys.CreateTextFile(zipFile , True) .Write "PK" & Chr(5) & Chr(6) & String(18,0) .Close End With Set objShell = CreateObject("Shell.Application") '対象フォルダを丸ごとZIPファイルにコピーする objShell.NameSpace(zipFile).CopyHere(targetFolder) numZip = 0 Do While numZip = 0 WScript.Sleep(250) numZip = objShell.NameSpace(zipFile).Items.count Loop Set objFileSys = Nothing Set objShell = Nothing |
targetFileをtargetFolderに書き換えだけです。multi_fileフォルダに、空フォルダ、ファイルとサブフォルダがあり、うまく圧縮できたことを確認できました。
まとめ
今回は、VBScriptでファイルをZIP圧縮する方法を紹介しました。やはり第三者ソフトウェアを利用したほうが楽だなとあらためて思いました。
最後まで付き合ってくれてありがとうございました。不明なところがあれば、是非コメントで連絡してください。
それでは、また!!
ディスカッション
コメント一覧
サンプル大変参考になりました。
ありがとうございました。
下記の行でエラーが出てしまい
.Write “PK” & Chr(5) & Chr(6) & String(18,0)
「&」 を 「&」 にしたら動きました。
通りすがり様
ご指摘ありがとうございます。
「&」がエスケープされてエラーが発生してしまいました。
直しまして、更新しました。
助かりました。ありがとうございました。