• 追加された行はこの色です。
  • 削除された行はこの色です。
#title(DotNetZip(Ionic Zip Library)を使ってZIP書庫のリスト表示などを行う)

#navi(.NET プログラミング研究)
#navi(.NETプログラミング研究)

#contents

*DotNetZip(Ionic Zip Library)を使ってZIP書庫のリスト表示などを行う [#z81e0928]

**書庫内のエントリのリストを表示する [#eb072568]

[[前回>../94]]のZIP書庫を展開する方法で紹介したように、ZipFileはZipEntryのコレクションとしての機能がありますので、書庫内のエントリ情報をZipEntryオブジェクトで取得するのは簡単です。ZipEntryオブジェクトにより、エントリ名やタイムスタンプ、圧縮前と後のサイズ、圧縮率、圧縮方法、コメント、CRCなどの情報を取得できます。

ZipFileオブジェクトをFor Each(foreach)でまわしてZipEntryを取得することで、ZIP書庫内にあるすべてのエントリの情報を表示する例を以下に示します。

#code(vbnet){{
'エントリのリストを表示するZIP書庫のパス 
Dim zipPath As String = "C:\test.zip"

'ZipFileオブジェクトを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    zipPath, System.Text.Encoding.GetEncoding("shift_jis"))
    '書庫にコメントが付いていれば表示する 
    If String.IsNullOrEmpty(zip.Comment) Then
        Console.WriteLine("コメント:{0}", zip.Comment)
    End If

    '書庫内のエントリを列挙する 
    For Each entry As Ionic.Zip.ZipEntry In zip
        If entry.IsDirectory Then
            'ディレクトリのとき 
            Console.WriteLine("ディレクトリ名:{0}", entry.FileName)
            Console.WriteLine()
        Else
            'ファイルのとき 
            Console.WriteLine("ファイル名:{0}", entry.FileName)
            Console.WriteLine("更新日時:{0}", entry.ModifiedTime)
            '圧縮前のサイズ 
            Console.WriteLine("サイズ:{0}バイト", entry.UncompressedSize)
            '圧縮されたサイズ 
            Console.WriteLine("格納サイズ:{0}バイト", entry.CompressedSize)
            Console.WriteLine("圧縮率:{0}%", entry.CompressionRatio)
            Console.WriteLine("CRC32:{0:X}", entry.Crc)
            Console.WriteLine("パスワード:{0}", entry.UsesEncryption)
            Console.WriteLine()
        End If
    Next
End Using
}}

#code(csharp){{
//エントリのリストを表示するZIP書庫のパス
string zipPath = @"C:\test.zip";

//ZipFileオブジェクトを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
    zipPath, System.Text.Encoding.GetEncoding("shift_jis")))
{
    //書庫にコメントが付いていれば表示する
    if (string.IsNullOrEmpty(zip.Comment))
    {
        Console.WriteLine("コメント:{0}", zip.Comment);
    }

    //書庫内のエントリを列挙する
    foreach (Ionic.Zip.ZipEntry entry in zip)
    {
        if (entry.IsDirectory)
        {
            //ディレクトリのとき
            Console.WriteLine("ディレクトリ名:{0}", entry.FileName);
            Console.WriteLine();
        }
        else
        {
            //ファイルのとき
            Console.WriteLine("ファイル名:{0}", entry.FileName);
            Console.WriteLine("更新日時:{0}", entry.ModifiedTime);
            //圧縮前のサイズ
            Console.WriteLine("サイズ:{0}バイト", entry.UncompressedSize);
            //圧縮されたサイズ
            Console.WriteLine("格納サイズ:{0}バイト", entry.CompressedSize);
            Console.WriteLine("圧縮率:{0}%", entry.CompressionRatio);
            Console.WriteLine("CRC32:{0:X}", entry.Crc);
            Console.WriteLine("パスワード:{0}", entry.UsesEncryption);
            Console.WriteLine();
        }
    }
}
}}

エントリの名前のリストだけであれば、ZipFile.EntryFileNamesプロパティでも取得できます。

#code(vbnet){{
'エントリのリストを表示するZIP書庫のパス 
Dim zipPath As String = "C:\test.zip"

'ZipFileオブジェクトを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    zipPath, System.Text.Encoding.GetEncoding("shift_jis"))
    'エントリ名を表示する 
    For Each entryName As String In zip.EntryFileNames
        Console.WriteLine(entryName)
    Next
End Using
}}

#code(csharp){{
//エントリのリストを表示するZIP書庫のパス
string zipPath = @"C:\test.zip";

//ZipFileオブジェクトを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
    zipPath, System.Text.Encoding.GetEncoding("shift_jis")))
{
    //エントリ名を表示する
    foreach (string entryName in zip.EntryFileNames)
    {
        Console.WriteLine(entryName);
    }
}
}}

***ある条件のエントリをリスト表示する [#c6237f91]

ZipFile.SelectEntriesメソッドを使用すると、エントリの名前、タイムスタンプ、サイズ、属性を条件として、条件に合ったエントリをZipEntryのコレクションとして取得できあます。

エントリを選別する条件としてSelectEntriesメソッドに渡すselectionCriteriaパラメータは、[[前々回>../93]]紹介したAddSelectedFilesメソッドのselectionCriteriaパラメータの書式と同じですので、詳しくはそちらをご覧ください。

下の例ではSelectEntriesメソッドを使って.txt拡張子のエントリを取得して、その名前を列挙しています。

#code(vbnet){{
'ZipFileオブジェクトを作成する
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    "C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis"))
    '.txt拡張子のエントリのコレクションを取得する 
    Dim entries As ICollection(Of Ionic.Zip.ZipEntry) = zip.SelectEntries("*.txt")

    '取得したエントリを列挙する 
    For Each entry As Ionic.Zip.ZipEntry In entries
        Console.WriteLine(entry.FileName)
    Next
End Using
}}

#code(csharp){{
//ZipFileオブジェクトを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
    @"C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis")))
{
    //.txt拡張子のエントリのコレクションを取得する
    ICollection<Ionic.Zip.ZipEntry> entries = zip.SelectEntries("*.txt");

    //取得したエントリを列挙する
    foreach (Ionic.Zip.ZipEntry entry in entries)
    {
        Console.WriteLine(entry.FileName);
    }
}
}}

補足:既存のZIP書庫を開いたのではなく、新しくZipFileを作成した時は、Saveメソッドで保存しなければSelectEntriesメソッドは使用できません。

**書庫内のテキストファイルの中身を閲覧する [#ub9e4ded]

[[前回>../94]]ZipEntry.Extractメソッドを使って書庫内のファイルを展開する方法を紹介しましたが、このようにファイルに展開するのではなく、内容をStreamとして取得することもできます。それには、ZipEntry.OpenReaderメソッドを使います。

下の例では、ZipEntry.OpenReaderメソッドでCrcCalculatorStreamを取得することで、ZIP書庫内の"readme.txt"の内容を表示しています。

#code(vbnet){{
'ZipFileオブジェクトを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    "C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis"))
    '"readme.txt"という名前のエントリを探す 
    Dim entry As Ionic.Zip.ZipEntry = zip("readme.txt")
    If entry IsNot Nothing Then
        'CrcCalculatorStreamを取得する 
        Using ccs As Ionic.Zlib.CrcCalculatorStream = entry.OpenReader()
            'Shift JISでエンコードして表示する 
            Using sr As New System.IO.StreamReader( _
                ccs, System.Text.Encoding.GetEncoding("shift_jis"))
                Console.WriteLine(sr.ReadToEnd())
            End Using
        End Using
    End If
End Using
}}

#code(csharp){{
//ZipFileオブジェクトを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
    @"C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis")))
{
    //"readme.txt"という名前のエントリを探す
    Ionic.Zip.ZipEntry entry = zip["readme.txt"];
    if (entry != null)
    {
        //CrcCalculatorStreamを取得する
        using (Ionic.Zlib.CrcCalculatorStream ccs = entry.OpenReader())
        {
            //Shift JISでエンコードして表示する
            using (System.IO.StreamReader sr = new System.IO.StreamReader(
                ccs, System.Text.Encoding.GetEncoding("shift_jis")))
            {
                Console.WriteLine(sr.ReadToEnd());
            }
        }
    }
}
}}

また、ZipEntry.ExtractメソッドでStreamに展開することもできますので、例えばMemoryStreamに展開することもできます。

下の例では、ZipEntry.ExtractメソッドでMemoryStreamにテキストファイルを展開しています。

#code(vbnet){{
'ZipFileオブジェクトを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    "C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis"))
    '"readme.txt"という名前のエントリを探す 
    Dim entry As Ionic.Zip.ZipEntry = zip("readme.txt")
    If entry IsNot Nothing Then
        Using ms As New System.IO.MemoryStream()
            'MemoryStreamに展開する 
            entry.Extract(ms)
            ms.Position = 0

            'Shift JISでエンコードして、表示する 
            Using sr As New System.IO.StreamReader( _
                ms, System.Text.Encoding.GetEncoding("shift_jis"))
                Console.WriteLine(sr.ReadToEnd())
            End Using
        End Using
    End If
End Using
}}

#code(csharp){{
//ZipFileオブジェクトを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
    @"C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis")))
{
    //"readme.txt"という名前のエントリを探す
    Ionic.Zip.ZipEntry entry = zip["readme.txt"];
    if (entry != null)
    {
        using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
        {
            //MemoryStreamに展開する
            entry.Extract(ms);
            ms.Position = 0;

            //Shift JISでエンコードして、表示する
            using (System.IO.StreamReader sr = new System.IO.StreamReader(
                ms, System.Text.Encoding.GetEncoding("shift_jis")))
            {
                Console.WriteLine(sr.ReadToEnd());
            }
        }
    }
}
}}

**書庫が正しいかテストする [#wab411a9]

ZIP書庫が壊れていないか確かめるには、ZipFile.IsZipFileメソッドを使います。IsZipFileメソッドにZIPファイルのみを指定すると、ZIPメタデータのチェックのみを行います。IsZipFileメソッドの2番目のパラメータにTrueを指定すると、すべてのファイルを展開して確認します。ただしこの場合、パスワードで保護されたファイルがあると、必ずFalseを返します。

#code(vbnet){{
'ZIPファイルをチェックする 
If Ionic.Zip.ZipFile.IsZipFile("C:\test.zip", True) Then
    Console.WriteLine("ZIPファイルに問題は見つかりませんでした")
End If
}}

#code(csharp){{
//ZIPファイルをチェックする
if (Ionic.Zip.ZipFile.IsZipFile(@"C:\test.zip", true))
{
    Console.WriteLine("ZIPファイルに問題は見つかりませんでした");
}
}}

ZipFile.IsZipFile以外にZipFile.CheckZipというメソッドもありますが、これはZIP書庫内のディレクトリに問題がないか調べるもののようです。CheckZipメソッドは問題が見つかれば修正するように指定することもできますし、FixZipDirectoryメソッドで修正することもできます。なおCheckZipとFixZipDirectoryメソッドは、ReducedバージョンのDotNetZipでは使用できません。

**既存の書庫にファイルを追加する [#b8030a8f]

既存の書庫にファイルを追加するには、ZipFile.ReadメソッドでZipFileオブジェクトを作成し、そのZipFileオブジェクトにAddFileメソッドなど([[前々号>../93]]で説明しています)でエントリを追加し、Saveメソッドで保存します。

AddFileメソッドでファイルを追加したときは、既存の書庫に同名のファイルが存在すると、例外がスローされます。もし同名のファイルがあったとしても例外をスローさせずに上書きするならば、AddFileメソッドではなく、UpdateFileメソッドを使用します。

下の例では、UpdateFileメソッドを使って書庫にファイルを追加しています。

#code(vbnet){{
'ZipFileオブジェクトを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read("C:\test.zip")
    '書庫にファイルを追加する。同名のエントリがあれば更新する。 
    zip.UpdateFile("C:\readme.txt")
    '保存する 
    zip.Save()
End Using
}}

#code(csharp){{
//ZipFileオブジェクトを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(@"C:\test.zip"))
{
    //書庫にファイルを追加する。同名のエントリがあれば更新する。
    zip.UpdateFile(@"C:\readme.txt");
    //保存する
    zip.Save();
}
}}

***既存の書庫の更新に日本語は使えない? [#g1ed23e2]

実は上のコードにはごまかしがありました。ZipFile.Readメソッドで書庫を開くとき、Encodingを指定しませんでした。私が試した限りでは、EncodingにShift JISを指定して書庫を開き、Saveメソッドで保存すると、書庫が壊れてしまいました。((完全に壊れるわけではありませんが、書庫への追加、削除などが一切できなくなります。さらに書庫に日本語のエントリが含まれているときは、日本語が文字化けします。))また、EncodingをShift JISとして作成したZIP書庫は、上のコードのようにEncodingを指定しないで開いてから更新しても書庫が壊れました。つまり、一般のアーカイバで作成した書庫もこの方法で更新すると壊れると思われます。その上、書庫内に日本語のエントリ名が存在すると、その書庫がUseUnicodeAsNecessaryプロパティをTrueにして作成されたものだとしても、更新すると書庫が壊れました。

つまり、日本語にエントリ名を一切使わず、Encodingを指定しないで作成したZIP書庫だけこの方法で更新できると考えて良さそうです。そのため、現時点では、実用性は極めて低いといえます。

この問題は、以下に紹介する例でも同様に存在します。

**書庫から指定したエントリを削除する [#p3cd6e45]

書庫から一つのエントリを削除するには、ZipFile.RemoveEntryメソッドを使います。RemoveEntryメソッドに削除したいエントリ名か、ZipEntryオブジェクトを渡せば、削除できます。

複数のエントリを削除するには、RemoveSelectedEntriesメソッドを使うと便利です。RemoveSelectedEntriesメソッドでは、[[前々回>../93]]紹介したAddSelectedFilesメソッドのselectionCriteriaパラメータの書式を使用して削除するファイルを選択できます。

以下の例では、これらのメソッドを使って書庫内のファイルを削除しています。

#code(vbnet){{
'ZipFileオブジェクトを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read("C:\test.zip")
    'readme.txtがあれば削除する 
    Dim entry As Ionic.Zip.ZipEntry = zip("readme.txt")
    If entry IsNot Nothing Then
        zip.RemoveEntry(entry)
    End If

    'または、次のようにもできる 
    Dim removeName As String = "readme2.txt"
    If zip.EntryFileNames.Contains(removeName) Then
        zip.RemoveEntry(removeName)
    End If

    '拡張子が.docのファイルを削除する 
    zip.RemoveSelectedEntries("*.doc")

    '保存する 
    zip.Save()
End Using
}}

#code(csharp){{
//ZipFileオブジェクトを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(@"C:\test.zip"))
{
    //readme.txtがあれば削除する
    Ionic.Zip.ZipEntry entry = zip["readme.txt"];
    if (entry != null)
    {
        zip.RemoveEntry(entry);
    }

    //または、次のようにもできる
    string removeName = "readme2.txt";
    if (zip.EntryFileNames.Contains(removeName))
    {
        zip.RemoveEntry(removeName);
    }

    //拡張子が.docのファイルを削除する
    zip.RemoveSelectedEntries("*.doc");

    //保存する
    zip.Save();
}
}}

**コメント [#e472b818]
- パスワード付き書庫のテストはどうすればいいのですか? -- TERRA &new{2015-02-10 (火) 23:36:51};

#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]] [[:Category/ASP.NET]],2009-12-28 (月) 00:16:56,DOBON!,2009-12-28 (月) 00:16:56,DOBON!)

[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]