ZIP書庫の作成、閲覧、展開を行う
ここでは、C#またはVB.NETでZip書庫を扱う(作成、閲覧、展開)方法を紹介します。
J#のクラスを使用する方法
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先が参考になります。
- Bug when using the java.util.zip classes to write zip files
- Zip and Unzip from a C# program using J# runtime
#ziplibを使用する方法
さらに別の方法として、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を使う方法があります。これに関しては、「遅延バインディングによりアンマネージDLL関数を呼び出す」をご覧ください。