ここでは、C#またはVB.NETでZip書庫を扱う(作成、閲覧、展開)方法を紹介します。
C#、VB.NETには標準ではZipを扱う方法が用意されていませんが、J#にはvjslib.dllというものがあり、この中のjava.util.zip名前空間にあるクラスを使うことにより、Zipを扱うことが出来ます。
この方法は、「Using the Zip Classes in the J# Class Libraries to Compress Files and Data with C#」で詳しく紹介されています。
この方法によりZip書庫を扱う具体的なコードを以下に示します。必ずvjslib.dllを参照に加えてください。(後ほど述べますが、現在この方法には多くの問題がありますので、ご注意ください。)
まずはZip書庫内のファイル(エントリ)の情報を列挙するコードです。
[VB.NET] '開くZIPファイルの設定 Dim zipPath As String = "C:/test.zip" '読み込む Dim fis As New java.io.FileInputStream(zipPath) Dim zis As New java.util.zip.ZipInputStream(fis) 'ZIP内のファイル情報を取得 While True Dim ze As java.util.zip.ZipEntry = zis.getNextEntry() If (ze Is Nothing) Then Exit While End If If Not ze.isDirectory() Then '情報を表示する Console.WriteLine("ファイル名 : {0}", ze.getName()) Console.WriteLine("サイズ : {0} bytes", ze.getSize()) Console.WriteLine("圧縮サイズ : {0} bytes", _ ze.getCompressedSize()) Console.WriteLine("CRC : {0:X}", ze.getCrc()) Dim [date] As New java.util.Date(ze.getTime()) Console.WriteLine("日時 : {0}", [date].ToString()) Console.WriteLine() End If End While '閉じる zis.close() fis.close()
[C#] //開くZIPファイルの設定 string zipPath = "C://test.zip"; //読み込む java.io.FileInputStream fis = new java.io.FileInputStream(zipPath); java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(fis); //ZIP内のファイル情報を取得 java.util.zip.ZipEntry ze; while ((ze = zis.getNextEntry()) != null) { if (!ze.isDirectory()) { //情報を表示する Console.WriteLine("ファイル名 : {0}", ze.getName()); Console.WriteLine("サイズ : {0} bytes", ze.getSize()); Console.WriteLine("圧縮サイズ : {0} bytes", ze.getCompressedSize()); Console.WriteLine("CRC : {0:X}", ze.getCrc()); java.util.Date date = new java.util.Date(ze.getTime()); Console.WriteLine("日時 : {0}", date.ToString()); Console.WriteLine(); } } //閉じる zis.close(); fis.close();
次はZip書庫内のファイルを展開するコードです。ここでは書庫内のすべてのファイルを展開しています。
[VB.NET] '展開するZIPファイルの設定 Dim zipPath As String = "C:/test.zip" '展開先のフォルダの設定 Dim extractDir As String = "C:/temp" '読み込む Dim fis As New java.io.FileInputStream(zipPath) Dim zis As New java.util.zip.ZipInputStream(fis) 'ZIP内のファイル情報を取得 While True Dim ze As java.util.zip.ZipEntry = 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.getName()) '展開先のフォルダ Dim destDir As String = System.IO.Path.Combine( _ extractDir, System.IO.Path.GetDirectoryName(ze.getName())) System.IO.Directory.CreateDirectory(destDir) '展開先のパス Dim destPath As String = _ System.IO.Path.Combine(destDir, fileName) 'FileOutputStreamの作成 Dim fos As New java.io.FileOutputStream(destPath) '書込み Dim buffer(8191) As System.SByte While True Dim len As Integer = zis.read(buffer, 0, buffer.Length) If len <= 0 Then Exit While End If fos.write(buffer, 0, len) End While '閉じる fos.close() End If End While zis.close() fis.close()
[C#] //展開するZIPファイルの設定 string zipPath = "C://test.zip"; //展開先のフォルダの設定 string extractDir = "C://temp"; //読み込む java.io.FileInputStream fis = new java.io.FileInputStream(zipPath); java.util.zip.ZipInputStream zis = new java.util.zip.ZipInputStream(fis); //ZIP内のファイル情報を取得 java.util.zip.ZipEntry ze; while ((ze = zis.getNextEntry()) != null) { if (!ze.isDirectory()) { //ファイル名 string fileName = System.IO.Path.GetFileName(ze.getName()); //展開先のフォルダ string destDir = System.IO.Path.Combine( extractDir, System.IO.Path.GetDirectoryName(ze.getName())); System.IO.Directory.CreateDirectory(destDir); //展開先のパス string destPath = System.IO.Path.Combine(destDir, fileName); //FileOutputStreamの作成 java.io.FileOutputStream fos = new java.io.FileOutputStream(destPath); //書込み sbyte[] buffer = new sbyte[8192]; int len; while ((len = zis.read(buffer, 0, buffer.Length)) > 0) { fos.write(buffer, 0, len); } //閉じる fos.close(); } } zis.close(); fis.close();
最後は指定したファイルを圧縮してZip書庫を作成するコードです。
[VB.NET] '作成するZIPファイルの設定 Dim zipPath As String = "C:/test.zip" '圧縮するファイルの設定 Dim filePaths As String() = _ { _ "C:/1.bmp", _ "C:/2.bmp" _ } 'ZipOutputStreamの作成 Dim fos As New java.io.FileOutputStream(zipPath) Dim zos As New java.util.zip.ZipOutputStream(fos) 'Zipにファイルを追加する Dim file As String For Each file In filePaths 'ZIPに追加するときのファイル名を決定する Dim f As String = System.IO.Path.GetFileName(file) 'ディレクトリを保持する時は次のようにする 'Dim f As String = file.Remove( _ ' 0, System.IO.Path.GetPathRoot(file).Length) 'f = f.Replace("/", "/") Dim ze As New java.util.zip.ZipEntry(f) ze.setMethod(java.util.zip.ZipEntry.DEFLATED) zos.putNextEntry(ze) 'FileInputStreamの作成 Dim fis As New java.io.FileInputStream(file) '書込み Dim buffer(8191) As System.SByte While True Dim len As Integer = fis.read(buffer, 0, buffer.Length) If len <= 0 Then Exit While End If zos.write(buffer, 0, len) End While '閉じる fis.close() zos.closeEntry() Next file zos.close() fos.close()
[C#] //作成するZIPファイルの設定 string zipPath = "C://test.zip"; //圧縮するファイルの設定 string[] filePaths = { "C://1.bmp", "C://2.bmp" }; //ZipOutputStreamの作成 java.io.FileOutputStream fos = new java.io.FileOutputStream(zipPath); java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(fos); //Zipにファイルを追加する foreach (string file in filePaths) { //ZIPに追加するときのファイル名を決定する string f = System.IO.Path.GetFileName(file); //ディレクトリを保持する時は次のようにする //string f = file.Remove( // 0, System.IO.Path.GetPathRoot(file).Length); //f = f.Replace("//","/"); java.util.zip.ZipEntry ze = new java.util.zip.ZipEntry(f); ze.setMethod(java.util.zip.ZipEntry.DEFLATED); zos.putNextEntry(ze); //FileInputStreamの作成 java.io.FileInputStream fis = new java.io.FileInputStream(file); //書込み sbyte[] buffer = new sbyte[8192]; int len; while((len = fis.read(buffer, 0, buffer.Length)) > 0) { zos.write(buffer, 0, len); } //閉じる fis.close(); zos.closeEntry(); } zos.close(); fos.close();
このようにjava.util.zip内のクラスを使うと簡単にZipの扱いができるようになりますが、.NET Framework 1.1以前では、このやり方は非常に多くの問題があることが分かっています(2.0では修正されています)。例えば、上記の方法でパス名に日本語を含むファイルを圧縮するとZip書庫は正常に作成されません。また、ZipEntry.setTimeで日時を指定できません。さらに、先に紹介した「Using the Zip Classes in the J# Class Libraries to Compress Files and Data with C#」のサンプルでは、サイズの大きいバイナリファイルを圧縮すると、エラーが発生します(new ZipFileでエラーが発生しますが、上記のコードではこれを使用していません)。これらのバグについては、次のURL先が参考になります。
さらに別の方法として、Zipを扱う有名なライブラリである#ziplibを使う方法があります。(記事を書いている時点でのバージョンは0.81。)#ziplibでは、パスワードの設定を行うこともできます。#ziplibを使ってZipを扱うサンプルを以下に紹介します。
まずはZip内のファイルを列挙するコードから。先ほどのjava.util.zipのサンプルと似ていますね。
[VB.NET] '開くZIPファイルの設定 Dim zipPath As String = "C:/test.zip" '読み込む Dim fs As New System.IO.FileStream( _ zipPath, System.IO.FileMode.Open, _ System.IO.FileAccess.Read, System.IO.FileShare.Read) Dim zis As New ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs) 'ZIP内のファイル情報を取得 Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry While True ze = zis.GetNextEntry() If ze Is Nothing Then Exit While End If If Not ze.IsDirectory Then '情報を表示する Console.WriteLine("ファイル名 : {0}", ze.Name) Console.WriteLine("サイズ : {0} bytes", ze.Size) Console.WriteLine("圧縮サイズ : {0} bytes", _ ze.CompressedSize) Console.WriteLine("CRC : {0:X}", ze.Crc) Console.WriteLine("日時 : {0}", ze.DateTime) Console.WriteLine() End If End While '閉じる zis.Close() fs.Close()
[C#] //開くZIPファイルの設定 string zipPath = "C://test.zip"; //読み込む System.IO.FileStream fs = new System.IO.FileStream( zipPath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); ICSharpCode.SharpZipLib.Zip.ZipInputStream zis = new ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs); //ZIP内のファイル情報を取得 ICSharpCode.SharpZipLib.Zip.ZipEntry ze; while ((ze = zis.GetNextEntry()) != null) { if (!ze.IsDirectory) { //情報を表示する Console.WriteLine("ファイル名 : {0}", ze.Name); Console.WriteLine("サイズ : {0} bytes", ze.Size); Console.WriteLine("圧縮サイズ : {0} bytes", ze.CompressedSize); Console.WriteLine("CRC : {0:X}", ze.Crc); Console.WriteLine("日時 : {0}", ze.DateTime); Console.WriteLine(); } } //閉じる zis.Close(); fs.Close();
次はZip書庫内のすべてのファイルを展開するコードです。これもjava.util.zipのサンプルと似ています。
[VB.NET] '展開するZIPファイルの設定 Dim zipPath As String = "C:/test.zip" '展開先のフォルダの設定 Dim extractDir As String = "C:/temp" '読み込む Dim fs As New System.IO.FileStream( _ zipPath, System.IO.FileMode.Open, _ System.IO.FileAccess.Read, System.IO.FileShare.Read) Dim zis As New ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs) 'パスワードが設定されているとき 'zis.Password = "pass" 'ZIP内のファイル情報を取得 Dim ze As ICSharpCode.SharpZipLib.Zip.ZipEntry While True 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, System.IO.FileShare.Write) Dim buffer(2047) As Byte 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() End If End While zis.Close() fs.Close()
[C#] //展開するZIPファイルの設定 string zipPath = "C://test.zip"; //展開先のフォルダの設定 string extractDir = "C://temp"; //読み込む System.IO.FileStream fs = new System.IO.FileStream( zipPath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); ICSharpCode.SharpZipLib.Zip.ZipInputStream zis = new ICSharpCode.SharpZipLib.Zip.ZipInputStream(fs); //パスワードが設定されているとき //zis.Password = "pass"; //ZIP内のファイル情報を取得 ICSharpCode.SharpZipLib.Zip.ZipEntry ze; 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, System.IO.FileShare.Write); byte[] buffer = new byte[2048]; int len; while ((len = zis.Read(buffer, 0, buffer.Length)) > 0) { writer.Write(buffer, 0, len); } //閉じる writer.Close(); } } zis.Close(); fs.Close();
最後にファイルを圧縮してZip書庫を作成するサンプルです。ここではヘッダでエントリの情報を設定していますが、これを省略すると、フッタに書き込まれます。また、パスワードを"pass"に設定しています。
[VB.NET] '作成するZIPファイルの設定 Dim zipPath As String = "C:/test.zip" '圧縮するファイルの設定 Dim filePaths As String() = _ { _ "C:/1.txt", _ "D:/2.txt" _ } Dim crc As New ICSharpCode.SharpZipLib.Checksums.Crc32 Dim writer As New System.IO.FileStream( _ zipPath, System.IO.FileMode.Create, _ System.IO.FileAccess.Write, System.IO.FileShare.Write) Dim zos As New ICSharpCode.SharpZipLib.Zip.ZipOutputStream(writer) '圧縮レベルを設定する zos.SetLevel(6) 'パスワードを設定する zos.Password = "pass" 'Zipにファイルを追加する Dim file As String For Each file In filePaths 'ZIPに追加するときのファイル名を決定する Dim f As String = System.IO.Path.GetFileName(file) 'ディレクトリを保持する時は次のようにする 'Dim f As String = file.Remove( _ ' 0, System.IO.Path.GetPathRoot(file).Length) 'f = f.Replace("/", "/") Dim ze As New ICSharpCode.SharpZipLib.Zip.ZipEntry(f) 'ヘッダを設定する 'ファイルを読み込む Dim fs As New System.IO.FileStream( _ file, System.IO.FileMode.Open, _ System.IO.FileAccess.Read, System.IO.FileShare.Read) Dim buffer(fs.Length) As Byte fs.Read(buffer, 0, buffer.Length) fs.Close() 'CRCを設定する crc.Reset() crc.Update(buffer) ze.Crc = crc.Value 'サイズを設定する ze.Size = buffer.Length '時間を設定する ze.DateTime = DateTime.Now '新しいエントリの追加を開始 zos.PutNextEntry(ze) '書き込む zos.Write(buffer, 0, buffer.Length) Next file zos.Close() writer.Close()
[C#] //作成するZIPファイルの設定 string zipPath = "C://test.zip"; //圧縮するファイルの設定 string[] filePaths = { @"C:/test1.txt", @"D:/Download/mt.txt" }; ICSharpCode.SharpZipLib.Checksums.Crc32 crc = new ICSharpCode.SharpZipLib.Checksums.Crc32(); System.IO.FileStream writer = new System.IO.FileStream( zipPath, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.Write); ICSharpCode.SharpZipLib.Zip.ZipOutputStream zos = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(writer); //圧縮レベルを設定する zos.SetLevel(6); //パスワードを設定する zos.Password = "pass"; //Zipにファイルを追加する foreach (string file in filePaths) { //ZIPに追加するときのファイル名を決定する string f = System.IO.Path.GetFileName(file); //ディレクトリを保持する時は次のようにする //string f = file.Remove( // 0, System.IO.Path.GetPathRoot(file).Length); //f = f.Replace("//","/"); ICSharpCode.SharpZipLib.Zip.ZipEntry ze = new ICSharpCode.SharpZipLib.Zip.ZipEntry(f); //ヘッダを設定する //ファイルを読み込む System.IO.FileStream fs = new System.IO.FileStream( file, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read); byte[] buffer = new byte[fs.Length]; fs.Read(buffer, 0, buffer.Length); fs.Close(); //CRCを設定する crc.Reset(); crc.Update(buffer); ze.Crc = crc.Value; //サイズを設定する ze.Size = buffer.Length; //時間を設定する ze.DateTime = DateTime.Now; //新しいエントリの追加を開始 zos.PutNextEntry(ze); //書き込む zos.Write(buffer, 0, buffer.Length); } zos.Close(); writer.Close();
これ以外の方法では、アンマネージDLLを使う方法があります。これに関しては、「遅延バインディングによりアンマネージDLL関数を呼び出す」をご覧ください。