• 追加された行はこの色です。
  • 削除された行はこの色です。
#title(DotNetZip(Ionic Zip Library)を使ってZIP書庫を展開する)

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

#contents

*DotNetZip(Ionic Zip Library)を使ってZIP書庫を展開する [#k31bfe3c]

[[前回>../83]]はDotNetZipを使ってZIP書庫を作成する方法を紹介しました。今回はDotNetZipを使ってZIP書庫を展開(解凍)する方法を中心に紹介します。

**ZIP書庫を展開する基本的な方法 [#mb071cd6]

ZIP書庫を展開する基本的な手順は、次のようになります。

+ZipFile.Readメソッドを使ってZipFileオブジェクトを作成する。
+For Each(foreach)やインデクサを使って、展開したいファイルを表すZipEntryをZipFileオブジェクトから探す。
+ZipEntry.Extractメソッドでファイルを展開する。なお、展開先フォルダは自動的に作成される。

このような方法でZIP書庫内のすべてのファイルを展開する例を以下に示します。

#code(vbnet){{
'展開するZIP書庫のパス 
Dim zipPath As String = "C:\test.zip"
'展開先のフォルダのパス 
Dim folderPath As String = "C:\temp"

'ZipFileを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    zipPath, System.Text.Encoding.GetEncoding("shift_jis"))
    'パスワードが設定されているときは、 
    'zip.Password = "password"
    'または、ZipEntry.ExtractWithPasswordメソッドで展開する 

    '展開先に同名のファイルがあれば上書きする 
    zip.ExtractExistingFile = _
        Ionic.Zip.ExtractExistingFileAction.OverwriteSilently
    'DoNotOverwriteで上書きしない。Throwで例外をスロー。既定値はThrow。 
    'ZipEntry.Extractに指定して展開することもできる 

    'フォルダ構造を無視する 
    'zip.FlattenFoldersOnExtract = True

    'ZIP書庫内のエントリを取得 
    For Each entry As Ionic.Zip.ZipEntry In zip
        'エントリを展開する 
        entry.Extract(folderPath)
    Next
End Using
}}

#code(csharp){{
//展開するZIP書庫のパス
string zipPath = @"C:\test.zip";
//展開先のフォルダのパス
string folderPath = @"C:\temp";

//ZipFileを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
    zipPath, System.Text.Encoding.GetEncoding("shift_jis")))
{
    //パスワードが設定されているときは、
    //zip.Password = "password";
    //または、ZipEntry.ExtractWithPasswordメソッドで展開する

    //展開先に同名のファイルがあれば上書きする
    zip.ExtractExistingFile =
        Ionic.Zip.ExtractExistingFileAction.OverwriteSilently;
    //DoNotOverwriteで上書きしない。Throwで例外をスロー。既定値はThrow。
    //ZipEntry.Extractに指定して展開することもできる

    //フォルダ構造を無視する
    //zip.FlattenFoldersOnExtract = true;

    //ZIP書庫内のエントリを取得
    foreach (Ionic.Zip.ZipEntry entry in zip)
    {
        //エントリを展開する
        entry.Extract(folderPath);
    }
}
}}

補足:ZIP書庫内のすべてのファイルを展開するには、ZipFile.ExtractAllメソッドを使うこともできます。

ZipFile.Readメソッドでなく、ZipFileのコンストラクタでも、既存のZIP書庫を開いてZipFileオブジェクトを作成することができます。しかしReadメソッドで文字コードを指定しないと、Shift JISなどでエンコードしたファイル名は文字化けします。一般的なアーカイバが作成するZIP書庫は通常Shift JISでファイル名がエンコードされていますので、ZipFile.Readメソッドを使う必要があります。

ただし、UseUnicodeAsNecessaryをtrueにして作成した書庫のファイルは、文字コードやUseUnicodeAsNecessaryを設定しなくても文字化けせずに展開できました。よってこの場合は、ZipFile.Readメソッドを使わなくても大丈夫でした。

残念ながら、私の試した限りでは、Readメソッドで文字コードを設定しても、UseUnicodeAsNecessaryをtrueにしても、日本語のコメントは文字化けしました。

***1つのファイルを展開する [#o310d5cf]

ZipFileのインデクサを使えば、エントリ名をキーにしてZipEntryを取得できます。以下にこの方法でファイル名が"readme.txt"のファイルを展開する例を示します。

#code(vbnet){{
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    "C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis"))
    '"readme.txt"のZipEntryを取得する 
    '見つからなかったときはNothingを返す 
    Dim entry As Ionic.Zip.ZipEntry = zip("readme.txt")
    'エントリを展開する 
    entry.Extract("C:\temp")
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"のZipEntryを取得する
    //見つからなかったときはnullを返す
    Ionic.Zip.ZipEntry entry = zip["readme.txt"];
    //エントリを展開する
    entry.Extract(@"C:\temp");
}
}}

***特定のファイルのみを展開する [#z2ad7c80]

[[前回>../83]]の特定のファイルのみを圧縮する方法で紹介したAddSelectedFilesメソッドと同じように、ExtractSelectedEntriesメソッドにより、特定のファイルのみを展開することができます。

ExtractSelectedEntriesメソッドの1番目のパラメータであるselectionCriteriaの書式は、特定のファイルのみを圧縮する方法で説明したselectionCriteriaの書式と同じですので、詳しくはそちらをご覧ください。

以下に、拡張子が".txt"のファイルのみを展開する例を示します。

#code(vbnet){{
'展開するZIP書庫のパス 
Dim zipPath As String = "C:\test.zip"

'展開するファイルの定義 
Dim selectionCriteria As String = "*.txt"
'展開するファイルが存在する書庫内のフォルダのパス 
Dim directoryPathInArchive As String = Nothing
'展開先のフォルダのパス 
Dim extractDirectory As String = "C:\temp"
'展開したファイルが既に存在していた時どうするか 
Dim extractExistingFile As Ionic.Zip.ExtractExistingFileAction = _
    Ionic.Zip.ExtractExistingFileAction.DoNotOverwrite

'ZipFileを作成する 
Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
    zipPath, System.Text.Encoding.GetEncoding("shift_jis"))
    '指定したファイルだけを展開する 
    zip.ExtractSelectedEntries(selectionCriteria, directoryPathInArchive, _
                               extractDirectory, extractExistingFile)
End Using
}}

#code(csharp){{
//展開するZIP書庫のパス
string zipPath = @"C:\test.zip";

//展開するファイルの定義
string selectionCriteria = @"*.txt";
//展開するファイルが存在する書庫内のフォルダのパス
string directoryPathInArchive = null;
//展開先のフォルダのパス
string extractDirectory = @"C:\temp";
//展開したファイルが既に存在していた時どうするか
Ionic.Zip.ExtractExistingFileAction extractExistingFile =
    Ionic.Zip.ExtractExistingFileAction.DoNotOverwrite;

//ZipFileを作成する
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
    zipPath, System.Text.Encoding.GetEncoding("shift_jis")))
{
    //指定したファイルだけを展開する
    zip.ExtractSelectedEntries(selectionCriteria, directoryPathInArchive,
        extractDirectory, extractExistingFile);
}
}}

**圧縮、展開時に進行状況を表示する [#a43b1e6b]

ZIP書庫を作成するときに進行状況を表示するには、SaveProgressイベントを使用します。SaveProgressイベントでは、どのファイルをどれだけ(何バイト)書き込んだかなどの情報を知ることができます。

ZIP書庫を展開するときは、ExtractProgressイベントで進行状況を知ることができます。ExtractProgressイベントでは、どのファイルを何バイト展開したかなどの情報を知ることができます。

ZipFileクラスにはこれらのイベント以外に、AddProgressとZipErrorという2つのイベントがあります。AddProgressイベントは、ZipFileのAddFileやAddDirectoryメソッドなどでファイルが追加されるときに発生します。AddDirectoryメソッドなどで大量のファイルを追加するときに、どのファイルが追加されたかを知るのに役に立ちます。ZipErrorイベントは、書庫を作成するとき、圧縮するファイルを開けないなどの理由でエラーが発生すると発生します。

補足:AddProgressイベントハンドラでAddProgressEventArgs.CancelをTrueにしても、ファイルの追加を中止できません。

SaveProgressとExtractProgressイベントを使って、圧縮、展開時に進行状況を表示する例を以下に示します。

#code(vbnet){{
'書庫を作成する 
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button1.Click
    Using zip As New Ionic.Zip.ZipFile( _
        System.Text.Encoding.GetEncoding("shift_jis"))
        'SaveProgressイベントハンドラを追加する 
        AddHandler zip.SaveProgress, AddressOf Me.zip_SaveProgress

        'フォルダを追加する 
        zip.AddDirectory("C:\doc")
        'ZIP書庫を作成する 
        zip.Save("C:\test.zip")
    End Using
End Sub

'書庫を展開する 
Private Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button2.Click
    Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
        "C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis"))
        'ExtractProgressイベントハンドラを追加する 
        AddHandler zip.ExtractProgress, AddressOf Me.zip_ExtractProgress

        'すべてのファイルを展開する 
        zip.ExtractAll("C:\temp", _
                       Ionic.Zip.ExtractExistingFileAction.OverwriteSilently)
    End Using
End Sub

'SaveProgressイベントハンドラ 
Private Sub zip_SaveProgress(ByVal sender As Object, _
                             ByVal e As Ionic.Zip.SaveProgressEventArgs)
    If e.EventType = _
        Ionic.Zip.ZipProgressEventType.Saving_Started Then
        '書庫の作成を開始 
        Console.WriteLine("{0} の作成開始", e.ArchiveName)
    ElseIf e.EventType = _
        Ionic.Zip.ZipProgressEventType.Saving_BeforeWriteEntry Then
        'エントリの書き込み開始 
        Console.WriteLine("{0} の書き込み開始", e.CurrentEntry.FileName)
    ElseIf e.EventType = _
        Ionic.Zip.ZipProgressEventType.Saving_EntryBytesRead Then
        'エントリを書き込み中 
        Console.WriteLine("{0}/{1} バイト 書き込みました", _
                          e.BytesTransferred, e.TotalBytesToTransfer)

        'e.Cancel = True
        'とすると、書庫の作成が中止される 
    ElseIf e.EventType = _
        Ionic.Zip.ZipProgressEventType.Saving_AfterWriteEntry Then
        'エントリの書き込み終了 
        Console.WriteLine("{0} の書き込み終了", e.CurrentEntry.FileName)
        Console.WriteLine("{0} 個中 {1} 個のエントリの書き込みが完了しました", _
                          e.EntriesTotal, e.EntriesSaved)
    ElseIf e.EventType = _
        Ionic.Zip.ZipProgressEventType.Saving_Completed Then
        '書庫の作成が完了 
        Console.WriteLine("{0} の作成終了", e.ArchiveName)
    End If
End Sub

'ExtractProgressイベントハンドラ 
Private Sub zip_ExtractProgress(ByVal sender As Object, _
                                ByVal e As Ionic.Zip.ExtractProgressEventArgs)
    If e.EventType = _
        Ionic.Zip.ZipProgressEventType.Extracting_BeforeExtractEntry Then
        'エントリの展開を開始 
        Console.WriteLine("{0} の展開を開始", e.CurrentEntry.FileName)
        Console.WriteLine("展開先:{0}", e.ExtractLocation)
    ElseIf e.EventType = _
        Ionic.Zip.ZipProgressEventType.Extracting_EntryBytesWritten Then
        'エントリを展開中 
        Console.WriteLine("{0}/{1} バイト展開しました", _
                          e.BytesTransferred, e.TotalBytesToTransfer)
    ElseIf e.EventType = _
        Ionic.Zip.ZipProgressEventType.Extracting_AfterExtractEntry Then
        'エントリの展開が終了 
        Console.WriteLine("{0} の展開が終了", e.CurrentEntry.FileName)
    End If
End Sub
}}

#code(csharp){{
//書庫を作成する
private void Button1_Click(object sender, EventArgs e)
{
    using (Ionic.Zip.ZipFile zip =
        new Ionic.Zip.ZipFile(System.Text.Encoding.GetEncoding("shift_jis")))
    {
        //SaveProgressイベントハンドラを追加する
        zip.SaveProgress += this.zip_SaveProgress;

        //フォルダを追加する
        zip.AddDirectory(@"C:\doc");
        //ZIP書庫を作成する
        zip.Save(@"C:\test.zip");
    }
}

//書庫を展開する
private void Button2_Click(object sender, EventArgs e)
{
    using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
        @"C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis")))
    {
        //ExtractProgressイベントハンドラを追加する
        zip.ExtractProgress += this.zip_ExtractProgress;

        //すべてのファイルを展開する
        zip.ExtractAll(@"C:\temp",
            Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
    }
}

//SaveProgressイベントハンドラ
private void zip_SaveProgress(
    object sender, Ionic.Zip.SaveProgressEventArgs e)
{
    if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Saving_Started)
    {
        //書庫の作成を開始
        Console.WriteLine("{0} の作成開始", e.ArchiveName);
    }
    else if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Saving_BeforeWriteEntry)
    {
        //エントリの書き込み開始
        Console.WriteLine("{0} の書き込み開始", e.CurrentEntry.FileName);
    }
    else if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Saving_EntryBytesRead)
    {
        //エントリを書き込み中
        Console.WriteLine("{0}/{1} バイト 書き込みました",
            e.BytesTransferred, e.TotalBytesToTransfer);

        //e.Cancel = true;
        //とすると、書庫の作成が中止される
    }
    else if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Saving_AfterWriteEntry)
    {
        //エントリの書き込み終了
        Console.WriteLine("{0} の書き込み終了", e.CurrentEntry.FileName);
        Console.WriteLine("{0} 個中 {1} 個のエントリの書き込みが完了しました",
            e.EntriesTotal, e.EntriesSaved);
    }
    else if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Saving_Completed)
    {
        //書庫の作成が完了
        Console.WriteLine("{0} の作成終了", e.ArchiveName);
    }
}

//ExtractProgressイベントハンドラ
private void zip_ExtractProgress(
    object sender, Ionic.Zip.ExtractProgressEventArgs e)
{
    if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Extracting_BeforeExtractEntry)
    {
        //エントリの展開を開始
        Console.WriteLine("{0} の展開を開始", e.CurrentEntry.FileName);
        Console.WriteLine("展開先:{0}", e.ExtractLocation);
    }
    else if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Extracting_EntryBytesWritten)
    {
        //エントリを展開中
        Console.WriteLine("{0}/{1} バイト展開しました",
            e.BytesTransferred, e.TotalBytesToTransfer);
    }
    else if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Extracting_AfterExtractEntry)
    {
        //エントリの展開が終了
        Console.WriteLine("{0} の展開が終了", e.CurrentEntry.FileName);
    }
}
}}

**展開したファイルを上書きするかユーザーに問い合わせる [#g76284d9]

デフォルトでは、展開先に同名のファイルが存在すると例外が発生します。この動作を変更するには、ZipFile.ExtractExistingFileプロパティを変更します。ExtractExistingFileプロパティをOverwriteSilentlyにすると常に上書きし、DoNotOverwriteにすると常に上書きしません。

もしファイルを上書きするかユーザーに問い合わせるようにするならば、ExtractExistingFileプロパティをInvokeExtractProgressEventにします。このようにすると、展開しようとしているファイルと同名のファイルが既に存在しているとき、ExtractProgressイベントが発生し、ExtractProgressEventArgs.EventTypeがExtracting_ExtractEntryWouldOverwriteになります。この時、ExtractProgressEventArgs.CurrentEntryプロパティで得られるZipEntryオブジェクトのExtractExistingFileプロパティを変更することで、上書きするかしないかを指定できます。

さらに、ExtractProgressEventArgs.Cancelプロパティをtrueにして展開をキャンセルすることもできます。この時、これ以降のすべてのファイルの展開がキャンセルされます。

上書きするかをメッセージボックスでユーザーに問い合わせ、「はい」ボタンを押したときは上書きし、「いいえ」ボタンを押したときは上書きせず、「キャンセル」ボタンを押したときは以降の展開をキャンセルするようにした例を以下に示します。

#code(vbnet){{
'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
    Handles Button1.Click
    '書庫を展開する 
    Using zip As Ionic.Zip.ZipFile = Ionic.Zip.ZipFile.Read( _
        "C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis"))
        'ExtractProgressイベントハンドラを追加する 
        AddHandler zip.ExtractProgress, AddressOf Me.zip_ExtractProgress
        'ExtractProgressイベントで上書きするか指定できるようにする 
        zip.ExtractExistingFile = _
            Ionic.Zip.ExtractExistingFileAction.InvokeExtractProgressEvent

        'すべてのファイルを展開する 
        zip.ExtractAll("C:\temp")
    End Using
End Sub

'ExtractProgressイベントハンドラ 
Private Sub zip_ExtractProgress(ByVal sender As Object, _
                                ByVal e As Ionic.Zip.ExtractProgressEventArgs)
    If e.EventType = _
        Ionic.Zip.ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite Then
        '展開するファイル名 
        Dim filePath As String = System.IO.Path.Combine( _
            e.ExtractLocation, e.CurrentEntry.FileName.Replace("/"c, "\"c))
        'ダイアログを表示する 
        Dim res As DialogResult = MessageBox.Show(Me, _
            "'" & filePath & "'はすでに存在します。" & vbCrLf & _
                "'はい'で上書き 'いいえ'で何もしない 'キャンセル'で中止", _
            "上書きの確認", _
            MessageBoxButtons.YesNoCancel, _
            MessageBoxIcon.Question)

        If res = DialogResult.Yes Then
            '上書きする 
            e.CurrentEntry.ExtractExistingFile = _
                Ionic.Zip.ExtractExistingFileAction.OverwriteSilently
        ElseIf res = DialogResult.No Then
            '上書きしない 
            e.CurrentEntry.ExtractExistingFile = _
                Ionic.Zip.ExtractExistingFileAction.DoNotOverwrite
        ElseIf res = DialogResult.Cancel Then
            '展開を中止する 
            e.Cancel = True
        End If
    End If
End Sub
}}

#code(csharp){{
//Button1のClickイベントハンドラ
private void Button1_Click(object sender, EventArgs e)
{
    //書庫を展開する
    using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(
        @"C:\test.zip", System.Text.Encoding.GetEncoding("shift_jis")))
    {
        //ExtractProgressイベントハンドラを追加する
        zip.ExtractProgress += zip_ExtractProgress;
        //ExtractProgressイベントで上書きするか指定できるようにする
        zip.ExtractExistingFile =
            Ionic.Zip.ExtractExistingFileAction.InvokeExtractProgressEvent;

        //すべてのファイルを展開する
        zip.ExtractAll(@"C:\temp");
    }
}

//ExtractProgressイベントハンドラ
private void zip_ExtractProgress(
    object sender, Ionic.Zip.ExtractProgressEventArgs e)
{
    if (e.EventType ==
        Ionic.Zip.ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite)
    {
        //展開するファイル名
        string filePath = System.IO.Path.Combine(
            e.ExtractLocation, e.CurrentEntry.FileName.Replace('/', '\\'));
        //ダイアログを表示する
        DialogResult res = MessageBox.Show(this,
            "'" + filePath + "'はすでに存在します。\n" +
                "'はい'で上書き 'いいえ'で何もしない 'キャンセル'で中止",
            "上書きの確認",
            MessageBoxButtons.YesNoCancel,
            MessageBoxIcon.Question);

        if (res == DialogResult.Yes)
        {
            //上書きする
            e.CurrentEntry.ExtractExistingFile =
                Ionic.Zip.ExtractExistingFileAction.OverwriteSilently;
        }
        else if (res == DialogResult.No)
        {
            //上書きしない
            e.CurrentEntry.ExtractExistingFile =
                Ionic.Zip.ExtractExistingFileAction.DoNotOverwrite;
        }
        else if (res == DialogResult.Cancel)
        {
            //展開を中止する
            e.Cancel = true;
        }
    }
}
}}

**コメント [#d0894924]
- 参考にさせていただきありがとうございます。 -- oo &new{2010-07-20 (火) 17:01:32};
- 質問があるのですが、プログレスバーで進捗状況を表示しながらZipファイルを作成したいのですが、サンプルコードを載せていただけたりできないでしょうか?お手数ではございますがよろしくお願いします。 -- oo &new{2010-07-20 (火) 17:03:37};
- パスワードのプロパティの設定順序気づかなかったので助かりました。 -- trsgr &new{2011-04-14 (木) 10:21:51};
- 読み取り専用ファイルの圧縮でエラーが出ます。対処方法を教えて下さい。 -- kanaisun &new{2014-11-28 (金) 21:03:25};

#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]],2009-12-15 (火) 01:13:27,DOBON!,2009-12-15 (火) 01:13:27,DOBON!)


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