ここでは、ZIP書庫を扱うことができるフリーのライブラリである#ziplib(SharpZipLib)を使う方法を紹介します。この記事を書いている時点での最新版のバージョンは0.85.5です。
なお、.NET FrameworkでZIP書庫を扱う方法は、これ以外にも、以下の記事で説明しているような方法もあります。
補足:#ziplibのライセンスは、基本的にはGNU General Public License(GPL)です。GPLのライブラリにリンクするプログラムは、静的なリンクはもちろん、動的にリンクする場合でさえGPLとしなければいけないとする意見があります。しかし#ziplibは、独立したモジュールとしてリンクして使用する場合は例外として、商用のクローズドソースアプリケーションでも使用できるようです。ただし、この説明が正しいという保証はできませんので、#ziplibのライセンスは必ずご自分でご確認ください。
#ziplibは「.NET Zip Library #ziplib (SharpZipLib)」からダウンロードできます。ここで紹介するサンプルでは、ダウンロードしたDLL(ICSharpCode.SharpZipLib.dll)がプロジェクトの参照設定に追加されているものとします。
FastZipクラスを使用すると、驚くほど簡単にZIP書庫の作成や展開を行うことができます。ZIP書庫を作成するにはCreateZipメソッドを、展開するにはExtractZipメソッドを使用します。
以下の例では、"C:\doc"フォルダ以下のすべてのファイルとフォルダを圧縮し、"C:\test.zip"というZIP書庫を作っています。
'作成するZIP書庫のパス 'ファイルが既に存在している場合は、上書きされる Dim zipFileName As String = "C:\test.zip" '圧縮するフォルダのパス Dim sourceDirectory As String = "C:\doc" 'サブディレクトリも圧縮するかどうか Dim recurse As Boolean = True 'FastZipオブジェクトの作成 Dim fastZip As New ICSharpCode.SharpZipLib.Zip.FastZip() '空のフォルダも書庫に入れるか。デフォルトはfalse fastZip.CreateEmptyDirectories = True 'ZIP64を使うか。デフォルトはDynamicで、状況に応じてZIP64を使う '(大きなファイルはZIP64でしか圧縮できないが、対応していないアーカイバもある) fastZip.UseZip64 = ICSharpCode.SharpZipLib.Zip.UseZip64.Dynamic 'パスワードを設定するには次のようにする 'fastZip.Password = "password" '圧縮してZIP書庫を作成 fastZip.CreateZip(zipFileName, sourceDirectory, recurse, Nothing, Nothing)
//作成するZIP書庫のパス //ファイルが既に存在している場合は、上書きされる string zipFileName = @"C:\test.zip"; //圧縮するフォルダのパス string sourceDirectory = @"C:\doc"; //サブディレクトリも圧縮するかどうか bool recurse = true; //FastZipオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.FastZip fastZip = new ICSharpCode.SharpZipLib.Zip.FastZip(); //空のフォルダも書庫に入れるか。デフォルトはfalse fastZip.CreateEmptyDirectories = true; //ZIP64を使うか。デフォルトはDynamicで、状況に応じてZIP64を使う //(大きなファイルはZIP64でしか圧縮できないが、対応していないアーカイバもある) fastZip.UseZip64 = ICSharpCode.SharpZipLib.Zip.UseZip64.Dynamic; //パスワードを設定するには次のようにする //fastZip.Password = "password"; //圧縮してZIP書庫を作成 fastZip.CreateZip(zipFileName, sourceDirectory, recurse, null, null);
CreateZipメソッドの4番目のパラメータにファイルのフィルタを指定することができます。これによって、例えば拡張子が".txt"のファイルだけを書庫に入れるといったように、書庫に入れるファイルを指定することができます。
フィルタの書式は、* や ? などのワイルドカードを使ったものではなく、正規表現を使ったものになります。例えば、拡張子が".txt"のファイルのみを対象とするならば、フィルタに"\.txt$"を指定します。なお、正規表現について詳しくは、「正規表現の基本」をご覧ください。
このフィルタと比較されるファイルのパスは、フルパスです。よって、例えば"readme.txt"という名前のファイルだけを対象としたいときに、フィルタを"^readme\.txt$"としてもうまくいきません。
補足:それでは"readme.txt"という名前のファイルだけを対象としたいときはフィルタにどのような記述をすればよいのかですが、これが意外と難しいです。普通に考えると"\\readme.txt$"(C#では、@"\\readme.txt$")で良さそうですが、これだとうまくいきません。"\\\\readme.txt$"(C#では、@"\\\\readme.txt$")とするとうまくいきます。なぜこのようにしないといけないのかは分かりません。(この問題は、v0.86.0.518で修正されました。v0.86.0.518からは逆に"\\\\readme.txt$"はダメで、"\\readme.txt$"としなければなりません。この修正について詳しくはこちら。)
このフィルタには、複数の正規表現パターンを指定することもできます。この場合、各パターンを";"で区切ります。さらに、除外するファイルを指定することもでき、このときは、正規表現パターンの先頭に"-"を付けます。同様に、追加するファイルを指定するときは、パターンの先頭に"+"を付けますが、何もつけなくても同じになります。
例えば、拡張子が".txt"か".rtf"のファイルを対象とし、"コピー"という文字が入っているファイルを対象から外すのであれば、フィルタを"\.txt$;\.rtf$;-コピー"(または、"+\.txt$;+\.rtf$;-コピー")とします。
ファイルのフィルタと同じように、フォルダのフィルタをCreateZipメソッドの5番目のパラメータとして指定することもできます。
フィルタをNoting(C#では、null)または空の文字列("")にすると、すべてのファイルまたはフォルダが対象になります。
ZIP書庫を展開するには、ExtractZipメソッドを使います。
以下に例を示します。ここでは、ZIP書庫"C:\test.zip"をフォルダ"C:\temp"に展開しています。
'展開するZIP書庫のパス Dim zipFileName As String = "C:\test.zip" '展開したファイルを保存するフォルダ(存在しないと作成される) Dim targetDirectory As String = "C:\temp" '展開するファイルのフィルタ Dim fileFilter As String = "" 'FastZipオブジェクトの作成 Dim fastZip As New ICSharpCode.SharpZipLib.Zip.FastZip() '属性を復元するか。デフォルトはfalse fastZip.RestoreAttributesOnExtract = True 'ファイル日時を復元するか。デフォルトはfalse fastZip.RestoreDateTimeOnExtract = True '空のフォルダも作成するか。デフォルトはfalse fastZip.CreateEmptyDirectories = True 'パスワードが設定されているとき 'パスワードが設定されている書庫をパスワードを指定せずに展開しようとすると、 ' 例外ZipExceptionがスローされる 'fastZip.Password = "password" 'ZIP書庫を展開する fastZip.ExtractZip(zipFileName, targetDirectory, fileFilter)
//展開するZIP書庫のパス string zipFileName = @"C:\test.zip"; //展開したファイルを保存するフォルダ(存在しないと作成される) string targetDirectory = @"C:\temp"; //展開するファイルのフィルタ string fileFilter = ""; //FastZipオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.FastZip fastZip = new ICSharpCode.SharpZipLib.Zip.FastZip(); //属性を復元するか。デフォルトはfalse fastZip.RestoreAttributesOnExtract = true; //ファイル日時を復元するか。デフォルトはfalse fastZip.RestoreDateTimeOnExtract = true; //空のフォルダも作成するか。デフォルトはfalse fastZip.CreateEmptyDirectories = true; //パスワードが設定されているとき //パスワードが設定されている書庫をパスワードを指定せずに展開しようとすると、 // 例外ZipExceptionがスローされる //fastZip.Password = "password"; //ZIP書庫を展開する fastZip.ExtractZip(zipFileName, targetDirectory, fileFilter);
上記の例では、展開するファイルがすでに存在していると、上書きされます。これを変更するには、ExtractZipメソッドの別のオーバーロードを使用します。
絶対に上書きしないようにするには、ExtractZipメソッドの3番目のパラメータをFastZip.Overwrite.Neverにして、4番目のパラメータをNoting(C#では、null)にします。
さらに、ユーザーに問い合わせるなどのように、状況に応じて上書きするかを決めるようにすることもできます。この場合、3番目のパラメータをFastZip.Overwrite.Promptとし、4番目のパラメータにFastZip.ConfirmOverwriteDelegateデリゲートを指定します。
このExtractZipメソッドのオーバーロードを使用する場合は、5番目のパラメータに展開するファイルのフィルタを、6番目のパラメータに展開するフォルダのフィルタを、7番目のパラメータにファイル日時を復元するかを指定します。この7番目のパラメータは、FastZip.RestoreDateTimeOnExtractプロパティと同じ意味ではないかと思われます。
以下の例では、ZIP書庫を展開するとき、ファイルを上書きする必要があるときはメッセージボックスを表示してユーザーに問い合わせるようにしています。
'Button1のClickイベントハンドラ Private Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button1.Click '展開するZIP書庫のパス Dim zipFileName As String = "C:\test.zip" '展開したファイルを保存するフォルダ(存在しないと作成される) Dim targetDirectory As String = "C:\temp" '展開するファイルのフィルタ Dim fileFilter As String = "" '展開するフォルダのフィルタ Dim directoryFilter As String = "" 'FastZipオブジェクトの作成 Dim fastZip As New ICSharpCode.SharpZipLib.Zip.FastZip() 'ZIP書庫を展開する fastZip.ExtractZip(zipFileName, _ targetDirectory, _ ICSharpCode.SharpZipLib.Zip.FastZip.Overwrite.Prompt, _ New ICSharpCode.SharpZipLib.Zip.FastZip.ConfirmOverwriteDelegate( _ AddressOf FastZipConfirmOverwrite), _ fileFilter, _ directoryFilter, _ fastZip.RestoreDateTimeOnExtract) End Sub '上書きするときはtrueを返す Private Function FastZipConfirmOverwrite(ByVal file As String) As Boolean Return System.Windows.Forms.MessageBox.Show( _ file & vbCrLf & "を上書きしますか?", _ "上書きの確認", _ System.Windows.Forms.MessageBoxButtons.YesNo) = DialogResult.Yes End Function
private void Button1_Click(object sender, EventArgs e) { //展開するZIP書庫のパス string zipFileName = @"C:\test.zip"; //展開したファイルを保存するフォルダ(存在しないと作成される) string targetDirectory = @"C:\temp"; //展開するファイルのフィルタ string fileFilter = ""; //展開するフォルダのフィルタ string directoryFilter = ""; //FastZipオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.FastZip fastZip = new ICSharpCode.SharpZipLib.Zip.FastZip(); //ZIP書庫を展開する fastZip.ExtractZip(zipFileName, targetDirectory, ICSharpCode.SharpZipLib.Zip.FastZip.Overwrite.Prompt, new ICSharpCode.SharpZipLib.Zip.FastZip.ConfirmOverwriteDelegate( FastZipConfirmOverwrite), fileFilter, directoryFilter, fastZip.RestoreDateTimeOnExtract); } //上書きするときはtrueを返す private bool FastZipConfirmOverwrite(string file) { return System.Windows.Forms.MessageBox.Show( file + "\nを上書きしますか?", "上書きの確認", System.Windows.Forms.MessageBoxButtons.YesNo) == DialogResult.Yes; }
補足:読み取り専用ファイルを上書きしようとすると、例外UnauthorizedAccessExceptionがスローされます。以下に紹介するFastZip.FileFailureプロパティを使用すると、エラーを出さないようにすることができます。
FastZipクラスでは、ファイルを圧縮(あるいは展開)する時に、その進行状況や、エラーが発生したことをコールバックメソッドで知ることができます。そのためには、FastZipEventsオブジェクトを作成し、適当なフィールドにコールバックメソッドを設定し、FastZipのコンストラクタにパラメータとして渡します。
FastZipEventsクラスには以下のようなフィールドが用意されており、コールバックメソッドを設定することができます。
フィールド名 | 説明 |
---|---|
ProcessFile | 1つのファイルの圧縮または展開を始める時に呼び出される。ScanEventArgsのNameプロパティは処理するファイルのフルパスになる。ContinueRunningプロパティをfalseにすると、これ以降のすべての処理がキャンセルされる。 |
Progress | 1つのファイルの圧縮または展開中に呼び出される。ScanEventArgsのPercentCompleteプロパティは進行状況のパーセント、Processedプロパティは処理したバイト数、Targetプロパティはファイルのバイト数となる。 |
CompletedFile | 1つのファイルの圧縮または展開が完了した時に呼び出される。 |
ProcessDirectory | 1つのフォルダを作成した時に呼び出される。(空のフォルダを圧縮または展開するときだけ呼び出される?)DirectoryEventArgsのContinueRunningプロパティをfalseにすると、これ以降のすべての処理がキャンセルされる。 |
FileFailure | ファイルの圧縮または展開に失敗した時に呼び出される。これが設定されていると、ファイルの処理に失敗してもExtractZipが例外をスローしない。ScanFailureEventArgsのContinueRunningプロパティをfalseにすると、これ以降のすべての処理がキャンセルされる。 | DirectoryFailure | フォルダの圧縮または展開に失敗した時に呼び出される。これが設定されていると、フォルダの処理に失敗してもExtractZipが例外をスローしない。ScanFailureEventArgsのContinueRunningプロパティをfalseにすると、これ以降のすべての処理がキャンセルされる。 |
これらすべてのコールバックメソッドを使用して書庫を作成する例を示します。
'Button1のClickイベントハンドラ Private Sub Button1_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Button1.Click '作成するZIP書庫のパス Dim zipFileName As String = "C:\test.zip" '圧縮するフォルダのパス Dim sourceDirectory As String = "C:\doc" 'サブディレクトリも圧縮するかどうか Dim recurse As Boolean = True 'FastZipEventsの作成 Dim fastZipEvents As New ICSharpCode.SharpZipLib.Zip.FastZipEvents() fastZipEvents.CompletedFile = _ New ICSharpCode.SharpZipLib.Core.CompletedFileHandler( _ AddressOf CompletedFile) fastZipEvents.DirectoryFailure = _ New ICSharpCode.SharpZipLib.Core.DirectoryFailureHandler( _ AddressOf DirectoryFailure) fastZipEvents.FileFailure = _ New ICSharpCode.SharpZipLib.Core.FileFailureHandler( _ AddressOf FileFailure) fastZipEvents.ProcessDirectory = _ New ICSharpCode.SharpZipLib.Core.ProcessDirectoryHandler( _ AddressOf ProcessDirectory) fastZipEvents.ProcessFile = _ New ICSharpCode.SharpZipLib.Core.ProcessFileHandler( _ AddressOf ProcessFile) fastZipEvents.Progress = _ New ICSharpCode.SharpZipLib.Core.ProgressHandler( _ AddressOf Progress) 'FastZipオブジェクトの作成 Dim fastZip As New ICSharpCode.SharpZipLib.Zip.FastZip(fastZipEvents) '空のフォルダも書庫に入れるか fastZip.CreateEmptyDirectories = True '圧縮してZIP書庫を作成 fastZip.CreateZip(zipFileName, sourceDirectory, recurse, Nothing, Nothing) End Sub '1つのファイルの圧縮、展開を始める時 Private Sub ProcessFile(ByVal sender As Object, _ ByVal e As ICSharpCode.SharpZipLib.Core.ScanEventArgs) Console.WriteLine("""{0}""の処理を開始", e.Name) End Sub '1つのファイルの圧縮、展開の進行状況 Private Sub Progress(ByVal sender As Object, _ ByVal e As ICSharpCode.SharpZipLib.Core.ProgressEventArgs) Console.WriteLine("{0}%({1}/{2})", e.PercentComplete, e.Processed, e.Target) End Sub '1つのファイルの圧縮、展開が完了した時 Private Sub CompletedFile(ByVal sender As Object, _ ByVal e As ICSharpCode.SharpZipLib.Core.ScanEventArgs) Console.WriteLine("""{0}""の処理が完了", e.Name) End Sub '1つのフォルダの圧縮、展開が完了した時 Private Sub ProcessDirectory(ByVal sender As Object, _ ByVal e As ICSharpCode.SharpZipLib.Core.DirectoryEventArgs) Console.WriteLine("フォルダ""{0}""の処理を開始", e.Name) End Sub 'ファイルの圧縮、展開でエラーが発生した時 Private Sub FileFailure(ByVal sender As Object, _ ByVal e As ICSharpCode.SharpZipLib.Core.ScanFailureEventArgs) 'e.ContinueRunning = false; 'とすると、以降の処理をキャンセル Console.WriteLine("""{0}""の処理中にエラー({1})が発生", _ e.Name, e.Exception.Message) End Sub 'フォルダの圧縮、展開でエラーが発生した時 Private Sub DirectoryFailure(ByVal sender As Object, _ ByVal e As ICSharpCode.SharpZipLib.Core.ScanFailureEventArgs) Console.WriteLine("フォルダ""{0}""の処理中にエラー({1})が発生", _ e.Name, e.Exception.Message) End Sub
//Button1のClickイベントハンドラ private void Button1_Click(object sender, EventArgs e) { //作成するZIP書庫のパス string zipFileName = @"C:\test.zip"; //圧縮するフォルダのパス string sourceDirectory = @"C:\doc"; //サブディレクトリも圧縮するかどうか bool recurse = true; //FastZipEventsの作成 ICSharpCode.SharpZipLib.Zip.FastZipEvents fastZipEvents = new ICSharpCode.SharpZipLib.Zip.FastZipEvents(); fastZipEvents.CompletedFile = new ICSharpCode.SharpZipLib.Core.CompletedFileHandler(CompletedFile); fastZipEvents.DirectoryFailure = new ICSharpCode.SharpZipLib.Core.DirectoryFailureHandler(DirectoryFailure); fastZipEvents.FileFailure = new ICSharpCode.SharpZipLib.Core.FileFailureHandler(FileFailure); fastZipEvents.ProcessDirectory = new ICSharpCode.SharpZipLib.Core.ProcessDirectoryHandler(ProcessDirectory); fastZipEvents.ProcessFile = new ICSharpCode.SharpZipLib.Core.ProcessFileHandler(ProcessFile); fastZipEvents.Progress = new ICSharpCode.SharpZipLib.Core.ProgressHandler(Progress); //FastZipオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.FastZip fastZip = new ICSharpCode.SharpZipLib.Zip.FastZip(fastZipEvents); //空のフォルダも書庫に入れるか fastZip.CreateEmptyDirectories = true; //圧縮してZIP書庫を作成 fastZip.CreateZip(zipFileName, sourceDirectory, recurse, null, null); } //1つのファイルの圧縮、展開を始める時 private void ProcessFile(Object sender, ICSharpCode.SharpZipLib.Core.ScanEventArgs e) { Console.WriteLine("\"{0}\"の処理を開始", e.Name); } //1つのファイルの圧縮、展開の進行状況 private void Progress(Object sender, ICSharpCode.SharpZipLib.Core.ProgressEventArgs e) { Console.WriteLine("{0}%({1}/{2})", e.PercentComplete, e.Processed, e.Target); } //1つのファイルの圧縮、展開が完了した時 private void CompletedFile(Object sender, ICSharpCode.SharpZipLib.Core.ScanEventArgs e) { Console.WriteLine("\"{0}\"の処理が完了", e.Name); } //1つのフォルダの圧縮、展開が完了した時 private void ProcessDirectory(Object sender, ICSharpCode.SharpZipLib.Core.DirectoryEventArgs e) { Console.WriteLine("フォルダ\"{0}\"の処理を開始", e.Name); } //ファイルの圧縮、展開でエラーが発生した時 private void FileFailure(Object sender, ICSharpCode.SharpZipLib.Core.ScanFailureEventArgs e) { Console.WriteLine("\"{0}\"の処理中にエラー({1})が発生", e.Name, e.Exception.Message); //e.ContinueRunning = false; //とすると、以降の処理をキャンセル } //フォルダの圧縮、展開でエラーが発生した時 private void DirectoryFailure(Object sender, ICSharpCode.SharpZipLib.Core.ScanFailureEventArgs e) { Console.WriteLine("フォルダ\"{0}\"の処理中にエラー({1})が発生", e.Name, e.Exception.Message); }
ここからは、FastZipクラスを使用しない方法です。
まずは、ZIP書庫内のファイルやディレクトリ(エントリ)を列挙する方法を紹介します。
ZipInputStreamクラスを使用して、ZIP書庫内のエントリを列挙することができます。エントリ情報は、GetNextEntryメソッドを使ってZipEntryオブジェクトとして取得します。
以下の例では、"C:\test.zip"内のエントリの情報を列挙しています。
'開くZIP書庫のパス Dim zipPath As String = "C:\test.zip" 'ZIP書庫を読み込む Dim fs As New System.IO.FileStream( _ zipPath, _ System.IO.FileMode.Open, _ System.IO.FileAccess.Read) 'ZipInputStreamオブジェクトの作成 Dim zis As New ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs) 'ZIP内のエントリを列挙 Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry While True 'ZipEntryを取得 ze = zis.GetNextEntry() If ze Is Nothing Then Exit While End If '情報を表示する If ze.IsFile Then 'ファイルのとき Console.WriteLine("名前 : {0}", ze.Name) Console.WriteLine("サイズ : {0} bytes", ze.Size) Console.WriteLine("格納サイズ : {0} bytes", ze.CompressedSize) Console.WriteLine("圧縮方法 : {0}", ze.CompressionMethod) Console.WriteLine("CRC : {0:X}", ze.Crc) Console.WriteLine("日時 : {0}", ze.DateTime) Console.WriteLine(); ElseIf ze.IsDirectory Then 'ディレクトリのとき Console.WriteLine("ディレクトリ名 : {0}", ze.Name) Console.WriteLine("日時 : {0}", ze.DateTime) Console.WriteLine() End If End While '閉じる zis.Close() fs.Close()
//開くZIP書庫のパス string zipPath = @"C:\test.zip"; //ZIP書庫を読み込む System.IO.FileStream fs = new System.IO.FileStream( zipPath, System.IO.FileMode.Open, System.IO.FileAccess.Read); //ZipInputStreamオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipInputStream zis = new ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs); //ZIP内のエントリを列挙 ICSharpCode.SharpZipLib.Zip.ZipEntry ze; //ZipEntryを取得 while ((ze = zis.GetNextEntry()) != null) { //情報を表示する if (ze.IsFile) { //ファイルのとき Console.WriteLine("名前 : {0}", ze.Name); Console.WriteLine("サイズ : {0} bytes", ze.Size); Console.WriteLine("格納サイズ : {0} bytes", ze.CompressedSize); Console.WriteLine("圧縮方法 : {0}", ze.CompressionMethod); Console.WriteLine("CRC : {0:X}", ze.Crc); Console.WriteLine("日時 : {0}", ze.DateTime); Console.WriteLine(); } else if (ze.IsDirectory) { //ディレクトリのとき Console.WriteLine("ディレクトリ名 : {0}", ze.Name); Console.WriteLine("日時 : {0}", ze.DateTime); Console.WriteLine(); } } //閉じる zis.Close(); fs.Close();
ZipFileクラスを使用することもできます。ZipFileクラスではFor EachでZipEntryを取得できますので、より分かりやすいかもしれません。
'開くZIP書庫 Dim zipPath As String = "C:\test.zip" 'ZipFileオブジェクトの作成 Dim zf As New ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath) 'ZIP内のエントリを列挙 Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry For Each ze In zf '情報を表示する If ze.IsFile Then 'ファイルのとき Console.WriteLine("名前 : {0}", ze.Name) ElseIf ze.IsDirectory Then 'ディレクトリのとき Console.WriteLine("ディレクトリ名 : {0}", ze.Name) End If Next
//開くZIP書庫 string zipPath = @"C:\test.zip"; //ZipFileオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipFile zf = new ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath); //ZIP内のエントリを列挙 foreach (ICSharpCode.SharpZipLib.Zip.ZipEntry ze in zf) { //情報を表示する if (ze.IsFile) { //ファイルのとき Console.WriteLine("名前 : {0}", ze.Name); } else if (ze.IsDirectory) { //ディレクトリのとき Console.WriteLine("ディレクトリ名 : {0}", ze.Name); } } //閉じる zf.Close();
ZipFileクラスはZipInputStreamクラスと違い、コメントを取得できます。ZIP書庫のコメントはZipFile.ZipFileCommentプロパティで、エントリのコメントはZipEntry.Commentプロパティで取得できます。ZipInputStreamクラスではZipEntry.Commentプロパティでコメントを取得することができません。
なおコメントの設定は、ZipFileクラスでもZipOutputStreamクラスでもできます。ZIP書庫のコメントはSetCommentメソッドで、エントリのコメントはZipEntry.Commentプロパティでできます。
展開の方法も、ZipInputStreamクラスを使った方法と、ZipFileクラスを使った方法を紹介します。
ZipInputStreamクラスを使用して、書庫内のすべてのエントリを展開する例を示します。先のエントリを列挙する方法と同じやり方で展開するエントリを探し、ZipInputStream.Readメソッドでその内容を読み込みます。
'展開するZIP書庫のパス Dim zipPath As String = "C:\test.zip" '展開先のフォルダのパス Dim extractDir As String = "C:\temp" 'ZIP書庫を読み込む Dim fs As New System.IO.FileStream( _ zipPath, _ System.IO.FileMode.Open, _ System.IO.FileAccess.Read) 'ZipInputStreamオブジェクトの作成 Dim zis As New ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs) 'パスワードが設定されているときは指定する 'zis.Password = "pass" 'ZIP内のエントリを列挙 Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry While True 'ZipEntryを取得 ze = zis.GetNextEntry() If ze Is Nothing Then Exit While End If If Not ze.IsDirectory Then '展開先のファイル名を決定 Dim fileName As String = System.IO.Path.GetFileName(ze.Name) '展開先のフォルダを決定 Dim destDir As String = System.IO.Path.Combine( _ extractDir, System.IO.Path.GetDirectoryName(ze.Name)) System.IO.Directory.CreateDirectory(destDir) '展開先のファイルのフルパスを決定 Dim destPath As String = System.IO.Path.Combine(destDir, fileName) '書き込み先のファイルを開く Dim writer As New System.IO.FileStream(destPath, _ System.IO.FileMode.Create, System.IO.FileAccess.Write) '展開するファイルを読み込む Dim buffer As Byte() = New Byte(2047) {} Dim len As Integer While True len = zis.Read(buffer, 0, buffer.Length) If len = 0 Then Exit While End If 'ファイルに書き込む writer.Write(buffer, 0, len) End While '閉じる writer.Close() Else 'フォルダのとき '作成するフォルダのフルパスを決定 Dim dirPath As String = System.IO.Path.Combine( _ extractDir, System.IO.Path.GetDirectoryName(ze.Name)) 'フォルダを作成 System.IO.Directory.CreateDirectory(dirPath) End If End While '閉じる zis.Close() fs.Close()
//展開するZIP書庫のパス string zipPath = @"C:\test.zip"; //展開先のフォルダのパス string extractDir = @"C:\temp"; //ZIP書庫を読み込む System.IO.FileStream fs = new System.IO.FileStream( zipPath, System.IO.FileMode.Open, System.IO.FileAccess.Read); //ZipInputStreamオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipInputStream zis = new ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs); //パスワードが設定されているときは指定する //zis.Password = "pass"; //ZIP内のエントリを列挙 ICSharpCode.SharpZipLib.Zip.ZipEntry ze; //ZipEntryを取得 while ((ze = zis.GetNextEntry()) != null) { if (!ze.IsDirectory) { //展開先のファイル名を決定 string fileName = System.IO.Path.GetFileName(ze.Name); //展開先のフォルダを決定 string destDir = System.IO.Path.Combine(extractDir, System.IO.Path.GetDirectoryName(ze.Name)); System.IO.Directory.CreateDirectory(destDir); //展開先のファイルのフルパスを決定 string destPath = System.IO.Path.Combine(destDir, fileName); //書き込み先のファイルを開く System.IO.FileStream writer = new System.IO.FileStream( destPath, System.IO.FileMode.Create, System.IO.FileAccess.Write); //展開するファイルを読み込む byte[] buffer = new byte[2048]; int len; while ((len = zis.Read(buffer, 0, buffer.Length)) > 0) { //ファイルに書き込む writer.Write(buffer, 0, len); } //閉じる writer.Close(); } else { //フォルダのとき //作成するフォルダのフルパスを決定 string dirPath = System.IO.Path.Combine(extractDir, System.IO.Path.GetDirectoryName(ze.Name)); //フォルダを作成 System.IO.Directory.CreateDirectory(dirPath); } } //閉じる zis.Close(); fs.Close();
ZipFileクラスを使った方法では、指定したエントリ(ファイル)のみを展開する例を示します。もちろんすべてのエントリを展開することもできます。GetEntryメソッドで展開するZipEntryを探し、GetInputStreamメソッドでエントリの内容を読み込むStreamを取得します。
'展開するZIP書庫のパス Dim zipPath As String = "C:\test.zip" '展開先のフォルダのパス Dim extractDir As String = "C:\temp" '展開するエントリ Dim extractFile As String = "readme.txt" 'ZipFileオブジェクトの作成 Dim zf As New ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath) '展開するエントリを探す Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry = zf.GetEntry(extractFile) If Not ze Is Nothing Then '展開先のファイル名を決定 Dim fileName As String = System.IO.Path.GetFileName(ze.Name) '展開先のフォルダを決定 Dim destDir As String = System.IO.Path.Combine( _ extractDir, System.IO.Path.GetDirectoryName(ze.Name)) System.IO.Directory.CreateDirectory(destDir) '展開先のファイルのフルパスを決定 Dim destPath As String = System.IO.Path.Combine(destDir, fileName) '展開するZIPエントリのStreamを取得 Dim reader As System.IO.Stream = zf.GetInputStream(ze) '書き込み先のファイルのStreamを取得 Dim writer As New System.IO.FileStream(destPath, _ System.IO.FileMode.Create, System.IO.FileAccess.Write) 'ファイルに書き込む Dim buffer As Byte() = New Byte(2047) {} Dim len As Integer While True len = reader.Read(buffer, 0, buffer.Length) If len = 0 Then Exit While End If writer.Write(buffer, 0, len) End While '閉じる writer.Close() reader.Close() End If '閉じる zf.Close()
//展開するZIP書庫のパス string zipPath = @"C:\test.zip"; //展開先のフォルダのパス string extractDir = @"C:\temp"; //展開するエントリ string extractFile = "readme.txt"; //ZipFileオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipFile zf = new ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath); //展開するエントリを探す ICSharpCode.SharpZipLib.Zip.ZipEntry ze = zf.GetEntry(extractFile); if (ze != null) { //展開先のファイル名を決定 string fileName = System.IO.Path.GetFileName(ze.Name); //展開先のフォルダを決定 string destDir = System.IO.Path.Combine(extractDir, System.IO.Path.GetDirectoryName(ze.Name)); System.IO.Directory.CreateDirectory(destDir); //展開先のファイルのフルパスを決定 string destPath = System.IO.Path.Combine(destDir, fileName); //展開するZIPエントリのStreamを取得 System.IO.Stream reader = zf.GetInputStream(ze); //書き込み先のファイルのStreamを取得 System.IO.FileStream writer = new System.IO.FileStream( destPath, System.IO.FileMode.Create, System.IO.FileAccess.Write); //ファイルに書き込む byte[] buffer = new byte[2048]; int len; while ((len = reader.Read(buffer, 0, buffer.Length)) > 0) { writer.Write(buffer, 0, len); } //閉じる writer.Close(); reader.Close(); } //閉じる zf.Close();
書庫内のファイルの内容を、ファイルとして展開することなく閲覧する方法は、上記のファイルを展開する方法とほぼ同じです。違いは、データをファイルに書き込まずに文字列に変換して表示する点だけです。
以下にZipFileクラスを使って書庫内の"readme.txt"ファイルの内容を表示する例を示します。
'ZIP書庫のパス Dim zipPath As String = "C:\test.zip" '閲覧するエントリ Dim extractFile As String = "readme.txt" 'ZipFileオブジェクトの作成 Dim zf As New ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath) '展開するエントリを探す Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry = zf.GetEntry(extractFile) If Not ze Is Nothing Then '閲覧するZIPエントリのStreamを取得 Dim reader As System.IO.Stream = zf.GetInputStream(ze) '文字コードを指定してStreamReaderを作成 Dim sr As New System.IO.StreamReader(reader, _ System.Text.Encoding.GetEncoding("Shift_JIS")) '文字列を取得して、表示 Console.Write(sr.ReadToEnd()) '閉じる sr.Close() reader.Close() End If '閉じる zf.Close()
//ZIP書庫のパス string zipPath = @"C:\test.zip"; //閲覧するエントリ string extractFile = "readme.txt"; //ZipFileオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipFile zf = new ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath); //展開するエントリを探す ICSharpCode.SharpZipLib.Zip.ZipEntry ze = zf.GetEntry(extractFile); if (ze != null) { //閲覧するZIPエントリのStreamを取得 System.IO.Stream reader = zf.GetInputStream(ze); //文字コードを指定してStreamReaderを作成 System.IO.StreamReader sr = new System.IO.StreamReader( reader, System.Text.Encoding.GetEncoding("Shift_JIS")); //文字列を取得して、表示 Console.Write(sr.ReadToEnd()); //閉じる sr.Close(); reader.Close(); } //閉じる zf.Close();
ZipOutputStreamクラスを使って、ZIP書庫を作成することができます。ZipEntryを作成してPutNextEntryメソッドに渡してから、Writeメソッドで書き込みます。
指定したフォルダ以下のファイルを圧縮してZIP書庫を作成する例を以下に示します。
'作成するZIP書庫のパス Dim zipPath As String = "C:\test.zip" '圧縮するファイルのあるフォルダのパス Dim zipFolder As String = "C:\doc" '書き込むZIP書庫のStream Dim writer As New System.IO.FileStream(zipPath, _ System.IO.FileMode.Create, System.IO.FileAccess.Write) 'ZipOutputStreamを作成 Dim zos As New ICSharpCode.SharpZipLib.Zip.ZipOutputStream(writer) '圧縮レベルを設定する。0は圧縮しない。9は最高圧縮。 zos.SetLevel(9) 'パスワードを設定する 'zos.Password = "pass" '圧縮するフォルダにあるファイルを取得 Dim files As String() = System.IO.Directory.GetFiles(zipFolder, _ "*", System.IO.SearchOption.AllDirectories) 'ファイル名からエントリ名を決定するためにZipNameTransformを使用する 'zipFolderを基にした相対パスをエントリ名にする Dim nameTrans As New ICSharpCode.SharpZipLib.Zip.ZipNameTransform(zipFolder) For Each file As String In files 'ZIP内のエントリの名前を決定する Dim f As String = nameTrans.TransformFile(file) 'ZipEntryを作成 Dim ze As New ICSharpCode.SharpZipLib.Zip.ZipEntry(f) 'エントリ情報を設定する。設定しなくてもよい Dim fi As New System.IO.FileInfo(file) '時間を設定する ze.DateTime = fi.LastAccessTime '属性を保持する ze.ExternalFileAttributes = CInt(fi.Attributes) 'サイズを設定する ze.Size = fi.Length 'エントリ名とコメントにunicode UTF8を使う 'ze.IsUnicodeText = True '新しいエントリの追加を開始 zos.PutNextEntry(ze) '圧縮するファイルを読み込む Dim fs As New System.IO.FileStream(file, _ System.IO.FileMode.Open, System.IO.FileAccess.Read) Dim buffer As Byte() = New Byte(2047) {} Dim len As Integer While True len = fs.Read(buffer, 0, buffer.Length) If len = 0 Then Exit While End If '書庫に書き込む zos.Write(buffer, 0, len) End While fs.Close() Next '閉じる zos.Finish() zos.Close() writer.Close()
//作成するZIP書庫のパス string zipPath = @"C:\test.zip"; //圧縮するファイルのあるフォルダのパス string zipFolder = @"C:\doc"; //書き込むZIP書庫のStream System.IO.FileStream writer = new System.IO.FileStream( zipPath, System.IO.FileMode.Create, System.IO.FileAccess.Write); //ZipOutputStreamを作成 ICSharpCode.SharpZipLib.Zip.ZipOutputStream zos = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(writer); //圧縮レベルを設定する。0は圧縮しない。9は最高圧縮。 zos.SetLevel(9); //パスワードを設定する //zos.Password = "pass"; //圧縮するフォルダにあるファイルを取得 string[] files = System.IO.Directory.GetFiles(zipFolder, "*", System.IO.SearchOption.AllDirectories); //ファイル名からエントリ名を決定するためにZipNameTransformを使用する //zipFolderを基にした相対パスをエントリ名にする ICSharpCode.SharpZipLib.Zip.ZipNameTransform nameTrans = new ICSharpCode.SharpZipLib.Zip.ZipNameTransform(zipFolder); foreach (string file in files) { //ZIP内のエントリの名前を決定する string f = nameTrans.TransformFile(file); //ZipEntryを作成 ICSharpCode.SharpZipLib.Zip.ZipEntry ze = new ICSharpCode.SharpZipLib.Zip.ZipEntry(f); //エントリ情報を設定する。設定しなくてもよい System.IO.FileInfo fi = new System.IO.FileInfo(file); //時間を設定する ze.DateTime = fi.LastAccessTime; //属性を保持する ze.ExternalFileAttributes = (int)fi.Attributes; //サイズを設定する ze.Size = fi.Length; //エントリ名とコメントにunicode UTF8を使う //ze.IsUnicodeText = true; //新しいエントリの追加を開始 zos.PutNextEntry(ze); //圧縮するファイルを読み込む System.IO.FileStream fs = new System.IO.FileStream( file, System.IO.FileMode.Open, System.IO.FileAccess.Read); byte[] buffer = new byte[2048]; int len; while ((len = fs.Read(buffer, 0, buffer.Length)) > 0) { //書庫に書き込む zos.Write(buffer, 0, len); } fs.Close(); } //閉じる zos.Finish(); zos.Close(); writer.Close();
補足:以前紹介していたサンプルではZipEntry.Crcを設定していましたが、最近のバージョンの#ziplibではこのようにしてZIP書庫を作成すると、一般的なアーカイバでこの書庫を開いたときにヘッダが壊れていると判断されてしまいます。
ZipFileクラスを使ってZIP書庫を作成するには、ZipFile.CreateメソッドでZipFileオブジェクトを作成し、Addメソッドでエントリを追加します。先のZipOutputStreamの例と同じようにAddメソッドにZipEntryを指定することもできますが、ファイルのパスだけ、あるいはファイルのパスとエントリ名だけを指定することもできます。以下の例では、ファイルのパスとエントリ名を指定しています。
'作成するZIP書庫のパス Dim zipPath As String = "C:\test.zip" '圧縮するファイルのあるフォルダのパス Dim zipFolder As String = "C:\doc" 'ZipFileオブジェクトの作成 Dim zf As ICSharpCode.SharpZipLib.Zip.ZipFile = _ ICSharpCode.SharpZipLib.Zip.ZipFile.Create(zipPath) '圧縮するフォルダにあるファイルを取得 Dim files As String() = System.IO.Directory.GetFiles(zipFolder, _ "*", System.IO.SearchOption.AllDirectories) 'ファイル名からエントリ名を決定するためにZipNameTransformを使用する Dim nameTrans As New ICSharpCode.SharpZipLib.Zip.ZipNameTransform(zipFolder) 'ZipFileの更新を開始 zf.BeginUpdate() Dim file As String For Each file In files 'ZIP内のエントリの名前を決定する Dim f As String = nameTrans.TransformFile(file) 'ZIP書庫にファイルを追加する zf.Add(file, f) Next 'ZipFileの更新をコミット zf.CommitUpdate() '閉じる zf.Close()
//作成するZIP書庫のパス string zipPath = @"C:\test.zip"; //圧縮するファイルのあるフォルダのパス string zipFolder = @"C:\doc"; //ZipFileオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipFile zf = ICSharpCode.SharpZipLib.Zip.ZipFile.Create(zipPath); //圧縮するフォルダにあるファイルを取得 string[] files = System.IO.Directory.GetFiles(zipFolder, "*", System.IO.SearchOption.AllDirectories); //ファイル名からエントリ名を決定するためにZipNameTransformを使用する ICSharpCode.SharpZipLib.Zip.ZipNameTransform nameTrans = new ICSharpCode.SharpZipLib.Zip.ZipNameTransform(zipFolder); //ZipFileの更新を開始 zf.BeginUpdate(); foreach (string file in files) { //ZIP内のエントリの名前を決定する string f = nameTrans.TransformFile(file); //ZIP書庫にファイルを追加する zf.Add(file, f); } //ZipFileの更新をコミット zf.CommitUpdate(); //閉じる zf.Close();
ZIP書庫を作成する方法と同じように、ZipFileクラスを使用すれば、すでにあるZIP書庫にファイルを追加することも簡単です。
'ZIP書庫のパス Dim zipPath As String = "C:\test.zip" '書庫に追加するファイルのパス Dim file As String = "C:\readme.txt" 'ZipFileオブジェクトの作成 Dim zf As New ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath) 'ZipFileの更新を開始 zf.BeginUpdate() 'ZIP内のエントリの名前を決定する Dim f As String = System.IO.Path.GetFileName(file) 'ZIP書庫にファイルを追加する zf.Add(file, f) 'ZipFileの更新をコミット zf.CommitUpdate() '閉じる zf.Close()
//ZIP書庫のパス string zipPath = @"C:\test.zip"; //書庫に追加するファイルのパス string file = @"C:\readme.txt"; //ZipFileオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipFile zf = new ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath); //ZipFileの更新を開始 zf.BeginUpdate(); //ZIP内のエントリの名前を決定する string f = System.IO.Path.GetFileName(file); //ZIP書庫にファイルを追加する zf.Add(file, f); //ZipFileの更新をコミット zf.CommitUpdate(); //閉じる zf.Close();
ZIP書庫からエントリを削除するには、ZipFile.Deleteメソッドを使うと簡単です。下の例ではZipFile.Deleteメソッドに削除するエントリ名を指定していますが、エントリが見つからないと例外がスローされます。削除するエントリが書庫に存在するか調べてから削除するならば、展開する方法で紹介したように、GetEntryメソッドでZipEntryオブジェクトを取得し、取得できればそのZipEntryオブジェクトをDeleteメソッドに渡すやり方もあります。
'ZIP書庫のパス Dim zipPath As String = "C:\test.zip" '削除するエントリの名前 Dim file As String = "readme.txt" 'ZipFileオブジェクトの作成 Dim zf As New ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath) 'ZipFileの更新を開始 zf.BeginUpdate() 'エントリを削除する zf.Delete(file) 'ZipFileの更新をコミット zf.CommitUpdate() '閉じる zf.Close()
//ZIP書庫のパス string zipPath = @"C:\test.zip"; //削除するエントリの名前 string file = "readme.txt"; //ZipFileオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipFile zf = new ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath); //ZipFileの更新を開始 zf.BeginUpdate(); //エントリを削除する zf.Delete(file); //ZipFileの更新をコミット zf.CommitUpdate(); //閉じる zf.Close();
ZIP書庫をテストするには、ZipFile.TestArchiveメソッドを使います。TestArchiveメソッドにfalseを渡すとヘッダだけを調べ、trueを渡すとヘッダとCRCのチェックを行うようです。
'テストするZIP書庫のパス Dim zipPath As String = "C:\test.zip" 'ZipFileオブジェクトの作成 Dim zf As New ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath) 'テストする If zf.TestArchive(True) Then Console.WriteLine("エラーはありませんでした。") Else Console.WriteLine("エラーがありました。") End If '閉じる zf.Close()
//テストするZIP書庫のパス string zipPath = @"C:\test.zip"; //ZipFileオブジェクトの作成 ICSharpCode.SharpZipLib.Zip.ZipFile zf = new ICSharpCode.SharpZipLib.Zip.ZipFile(zipPath); //テストする if (zf.TestArchive(true)) { Console.WriteLine("エラーはありませんでした。"); } else { Console.WriteLine("エラーがありました。"); } //閉じる zf.Close();
さらにTestArchiveメソッドの別のオーバーロードを使えば、テストの進行状況や、エラーの出た箇所などを知ることもできますが、ここでは省略します。
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。