ZipArchiveクラスはZIPアーカイブの作成・展開・編集を行うためのクラス。 .NET Frameworkでは、4.5以降でSystem.IO.Compression.dllを参照に追加することでZipArchiveクラスを使用できる。

ZipArchiveクラス

ZipArchiveクラスはインスタンスを作成する際にStreamZipArchiveModeを指定する。 ZipArchiveクラスでできる操作と動作は指定したZipArchiveModeによって次のように変わる。

ZipArchiveModeとZipArchiveクラスの動作
ZipArchiveMode ZipArchiveクラスの動作
ZipArchiveMode.Read ZIPアーカイブを読み込む。
コンストラクタで指定したStreamをZIPアーカイブとして読み込み、展開する。
ZipArchiveMode.Create ZIPアーカイブを作成する。
ZIPアーカイブを作成し、アーカイブに追加された各エントリを圧縮した上で、コンストラクタで指定したStreamに書き込む。
ZipArchiveMode.Update ZIPアーカイブを更新する。
ReadとCreateの操作の両方を行う。
StreamからZIPアーカイブを読み込んで展開し、アーカイブの内容を変更したのち、再び圧縮してStreamに書き込む。

ZipArchiveクラスのコンストラクタでは、エントリ名(ZIPアーカイブ内のファイル名)で使用するエンコーディングをEncodingで指定することができる。 (§.エントリ名のエンコーディング (entryNameEncoding))

ZIPアーカイブの展開

ZipArchiveクラスを使ったZIPアーカイブの展開は次の順序で行う。

  1. ZIPアーカイブのStreamを開く
  2. 開いたStreamとZipArchiveMode.Readを指定してZipArchiveを作成する
  3. ZipArchive.EntriesプロパティもしくはZipArchive.GetEntryメソッドで展開したいファイルのZipArchiveEntryを取得する
  4. 取得したZipArchiveEntryのOpenメソッドメソッドを呼び出してエントリのStreamを取得する
  5. 取得したエントリのStreamから読み込むことで内容が展開される

次の例は、ZIPアーカイブを展開し、フォルダ構造を維持したまますべてのエントリを展開するもの。 なお、ZipFile.ExtractToDirectoryメソッドを使うと、以下の例と同等のコードをメソッド呼び出し1つで実現できる。 また、ファイルへの展開には拡張メソッドExtractToFileを使うこともできる。

using System;
using System.IO;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // ZIPアーカイブから展開したファイルを保存するディレクトリ
    const string extractDirectory = "extract";

    // sample.zipを開く
    using (var zipStream = File.OpenRead("sample.zip")) {
      // ストリームを読み込みZIPアーカイブを展開するためのZipArchiveを作成
      using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Read)) {
        // ZIPアーカイブ内の全エントリを列挙
        foreach (var entry in archive.Entries) {
          // エントリのアーカイブ内での相対パス名、サイズ、最終更新日を表示
          Console.Write("{0} ({1:N0} bytes, {2})", entry.FullName, entry.Length, entry.LastWriteTime);

          // エントリの展開先となるファイル名を作成
          var extractToFileName = Path.Combine(extractDirectory, entry.FullName);

          // サブディレクトリが存在しない場合は、先に作成しておく
          var extractToSubDirectory = Path.GetDirectoryName(extractToFileName);

          if (!Directory.Exists(extractToSubDirectory))
            Directory.CreateDirectory(extractToSubDirectory);

          // エントリを展開してファイルに保存する
          using (var entryStream = entry.Open()) {
            using (var extractToStream = File.Create(extractToFileName)) {
              entryStream.CopyTo(extractToStream);
            }
          }

          Console.WriteLine(" -> extracted to '{0}'", extractToFileName);
        }
      }
    }
  }
}
Imports System
Imports System.IO
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' ZIPアーカイブから展開したファイルを保存するディレクトリ
    Const extractDirectory As String = "extract"

    ' sample.zipを開く
    Using zipStream As Stream = File.OpenRead("sample.zip")
      ' ストリームを読み込みZIPアーカイブを展開するためのZipArchiveを作成
      Using archive As New ZipArchive(zipStream, ZipArchiveMode.Read)
        ' ZIPアーカイブ内の全エントリを列挙
        For Each entry As ZipArchiveEntry In archive.Entries
          ' エントリのアーカイブ内での相対パス名、サイズ、最終更新日を表示
          Console.Write("{0} ({1:N0} bytes, {2})", entry.FullName, entry.Length, entry.LastWriteTime)

          ' エントリの展開先となるファイル名を作成
          Dim extractToFileName As String = Path.Combine(extractDirectory, entry.FullName)

          ' サブディレクトリが存在しない場合は、先に作成しておく
          Dim extractToSubDirectory As String = Path.GetDirectoryName(extractToFileName)

          If Not Directory.Exists(extractToSubDirectory) Then Directory.CreateDirectory(extractToSubDirectory)

          ' エントリを展開してファイルに保存する
          Using entryStream As Stream = entry.Open()
            Using extractToStream As Stream = File.Create(extractToFileName)
              entryStream.CopyTo(extractToStream)
            End Using
          End Using

          Console.WriteLine(" -> extracted to '{0}'", extractToFileName)
        Next
      End Using
    End Using
  End Sub
End Class
実行結果例
sample.txt (13 bytes, 2013/03/22 0:29:52 +09:00) -> extracted to 'extract\sample.txt'
サンプル.txt (21 bytes, 2013/03/22 0:29:52 +09:00) -> extracted to 'extract\サンプル.txt'
フォルダ1\a.txt (1,024 bytes, 2013/03/22 0:29:52 +09:00) -> extracted to 'extract\フォルダ1\a.txt'
フォルダ1\b.txt (1,024 bytes, 2013/03/22 0:29:52 +09:00) -> extracted to 'extract\フォルダ1\b.txt'
フォルダ1\c.txt (1,024 bytes, 2013/03/22 0:29:52 +09:00) -> extracted to 'extract\フォルダ1\c.txt'

ZIPアーカイブの作成

ZipArchiveクラスを使ったZIPアーカイブの作成は次の順序で行う。

  1. ZIPアーカイブを書き込むためのStreamを開く
  2. 開いたStreamとZipArchiveMode.Createを指定してZipArchiveを作成する
  3. ZipArchive.CreateEntryメソッドでZipArchiveEntryを作成する
  4. 作成したZipArchiveEntryのOpenメソッドメソッドを呼び出してエントリのStreamを取得する
  5. 取得したエントリのStreamに書き込む事で圧縮して格納される
  6. 必要に応じてファイル属性・パーミッションを指定する (§.ファイル属性・パーミッションの指定 (ExternalAttributes))

次の例は、フォルダ構造を維持したままフォルダ内のファイルを圧縮してZIPアーカイブを作成するもの。 なお、ZipFile.CreateFromDirectoryメソッドを使うと、以下の例と同等のコードをメソッド呼び出し1つで実現できる。 また、ファイルからZipArchiveEntryを作成するには拡張メソッドCreateEntryFromFileを使うこともできる。

using System;
using System.IO;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // ZIPアーカイブとして圧縮したいファイルが存在するディレクトリ
    const string sourceDirectory = "source";

    // ZIPアーカイブとして圧縮するテスト用のファイルを用意する
    CreateTestFiles(sourceDirectory);

    // sample.zipを作成して開く
    using (var zipStream = File.Create("sample.zip")) {
      // ZIPアーカイブを作成してストリームに書き込むためのZipArchiveを作成
      using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Create)) {
        // 相対パスを取得するために、ディレクトリのフルパスを区切り文字付きで取得しておく
        var directory = Path.GetFullPath(sourceDirectory) + Path.DirectorySeparatorChar;

        // ディレクトリ内にある全ての階層のファイルを列挙
        foreach (var file in Directory.EnumerateFiles(sourceDirectory, "*", SearchOption.AllDirectories)) {
          // エントリ名として使用するためにルートディレクトリからの相対パスを取得
          var entryName = Path.GetFullPath(file).Replace(directory, string.Empty);

          // エントリを作成
          var entry = archive.CreateEntry(entryName);

          // ファイルを開いてエントリに書き込む (圧縮する)
          using (var sourceStream = File.OpenRead(file)) {
            using (var entryStream = entry.Open()) {
              sourceStream.CopyTo(entryStream);
            }
          }

          Console.WriteLine("{0} -> {1}", file, entryName);
        }
      }
    }
  }

  private static void CreateTestFiles(string dir)
  {
    if (Directory.Exists(dir))
      Directory.Delete(dir, true);

    Directory.CreateDirectory(dir);

    File.WriteAllText(Path.Combine(dir, "sample.txt"), "Hello, world!");
    File.WriteAllText(Path.Combine(dir, "サンプル.txt"), "こんにちは世界");

    var subdir = Path.Combine(dir, "フォルダ1");

    Directory.CreateDirectory(subdir);

    File.WriteAllText(Path.Combine(subdir, "a.txt"), new string('a', 1024));
    File.WriteAllText(Path.Combine(subdir, "b.txt"), new string('b', 1024));
    File.WriteAllText(Path.Combine(subdir, "c.txt"), new string('c', 1024));
  }
}
Imports System
Imports System.IO
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' ZIPアーカイブとして圧縮したいファイルが存在するディレクトリ
    Const sourceDirectory As String = "source"

    ' ZIPアーカイブとして圧縮するテスト用のファイルを用意する
    CreateTestFiles(sourceDirectory)

    ' sample.zipを作成して開く
    Using zipStream As Stream = File.Create("sample.zip")
      ' ZIPアーカイブを作成してストリームに書き込むためのZipArchiveを作成
      Using archive As New ZipArchive(zipStream, ZipArchiveMode.Create)
        ' 相対パスを取得するために、ディレクトリのフルパスを区切り文字付きで取得しておく
        Dim dir As String = Path.GetFullPath(sourceDirectory) + Path.DirectorySeparatorChar

        ' ディレクトリ内にある全ての階層のファイルを列挙
        For Each f As String In Directory.EnumerateFiles(sourceDirectory, "*", SearchOption.AllDirectories)
          ' エントリ名として使用するためにルートディレクトリからの相対パスを取得
          Dim entryName As String = Path.GetFullPath(f).Replace(dir, String.Empty)

          ' エントリを作成
          Dim entry As ZipArchiveEntry = archive.CreateEntry(entryName)

          ' ファイルを開いてエントリに書き込む (圧縮する)
          Using sourceStream As Stream = File.OpenRead(f)
            Using entryStream As Stream = entry.Open()
              sourceStream.CopyTo(entryStream)
            End Using
          End Using

          Console.WriteLine("{0} -> {1}", f, entryName)
        Next
      End Using
    End Using
  End Sub

  Private Shared Sub CreateTestFiles(ByVal dir As String)
    If Not Directory.Exists(dir) Then Directory.Delete(dir, True)

    Directory.CreateDirectory(dir)

    File.WriteAllText(Path.Combine(dir, "sample.txt"), "Hello, world!")
    File.WriteAllText(Path.Combine(dir, "サンプル.txt"), "こんにちは世界")

    Dim subdir As String = Path.Combine(dir, "フォルダ1")

    Directory.CreateDirectory(subdir)

    File.WriteAllText(Path.Combine(subdir, "a.txt"), New String("a"c, 1024))
    File.WriteAllText(Path.Combine(subdir, "b.txt"), New String("b"c, 1024))
    File.WriteAllText(Path.Combine(subdir, "c.txt"), New String("c"c, 1024))
  End Sub
End Class
実行結果
source\sample.txt -> sample.txt
source\サンプル.txt -> サンプル.txt
source\フォルダ1\a.txt -> フォルダ1\a.txt
source\フォルダ1\b.txt -> フォルダ1\b.txt
source\フォルダ1\c.txt -> フォルダ1\c.txt

圧縮レベルの指定 (CompressionLevel)

ZipArchive.CreateEntryメソッドでZipArchiveEntryを作成する際、同時に引数で圧縮レベルを指定することができる。 圧縮レベルの指定に使うCompressionLevel列挙体の値には次のようなものがある。

CompressionLevel
CompressionLevel 圧縮レベル
CompressionLevel.Optimal 圧縮率優先
CompressionLevel.Fastest 圧縮速度優先
CompressionLevel.NoCompression 無圧縮

ZIPアーカイブの変更・更新

ZipArchiveクラスを使ったZIPアーカイブの変更・更新は次の順序で行う。

  1. ZIPアーカイブを読み込むためのStreamを開く (このStreamは読み書き可能である必要がある)
  2. 開いたStreamとZipArchiveMode.Updateを指定してZipArchiveを作成する
  3. ZIPアーカイブの作成の場合と同様の手順でZipArchiveEntryを作成してファイルを追加する
  4. もしくはZIPアーカイブの展開の場合と同様の手順でZipArchiveEntryを取得し、Deleteメソッドで不要なエントリを削除する
  5. 変更した内容はStreamに書き込まれてZIPアーカイブは更新される

次の例は、ZIPアーカイブを開いて、ZIPアーカイブ内に格納されているフォルダを削除するもの。

using System;
using System.IO;
using System.IO.Compression;
using System.Linq;

class Sample {
  static void Main()
  {
    // 既存のZIPアーカイブsample.zipを開く
    using (var zipStream = File.Open("sample.zip", FileMode.Open)) {
      // ストリームを読み込みZIPアーカイブを変更・更新するためのZipArchiveを作成
      using (var archive = new ZipArchive(zipStream, ZipArchiveMode.Update)) {
        // ZIPアーカイブ内の全エントリを列挙
        //   ZipArchiveEntry.Deleteメソッドでエントリを削除するとZipArchive.Entriesからも
        //   削除されるが、foreach内でDeleteすると列挙中にコレクションを変更することになる。
        //   そのため、ToArray()メソッドで配列に変換するなどして直接ZipArchive.Entriesの
        //   コレクションを列挙しないようにする。
        foreach (var entry in archive.Entries.ToArray()) {
          // エントリ名(相対パス)が"フォルダ1\"で始まるエントリを削除
          // (アーカイブ内のフォルダを削除する)
          if (entry.FullName.StartsWith("フォルダ1\\")) {
            entry.Delete();

            Console.WriteLine("deleted '{0}'", entry.FullName);
          }
        }
      }
    }
  }
}
Imports System
Imports System.IO
Imports System.IO.Compression
Imports System.Linq

Class Sample
  Shared Sub Main()
    ' 既存のZIPアーカイブsample.zipを開く
    Using zipStream As Stream = File.Open("sample.zip", FileMode.Open)
      ' ストリームを読み込みZIPアーカイブを変更・更新するためのZipArchiveを作成
      Using archive As New ZipArchive(zipStream, ZipArchiveMode.Update)
        ' ZIPアーカイブ内の全エントリを列挙
        '   ZipArchiveEntry.Deleteメソッドでエントリを削除するとZipArchive.Entriesからも
        '   削除されるが、foreach内でDeleteすると列挙中にコレクションを変更することになる。
        '   そのため、ToArray()メソッドで配列に変換するなどして直接ZipArchive.Entriesの
        '   コレクションを列挙しないようにする。
        For Each entry As ZipArchiveEntry In archive.Entries.ToArray()
          ' エントリ名(相対パス)が"フォルダ1\"で始まるエントリを削除
          ' (アーカイブ内のフォルダを削除する)
          If entry.FullName.StartsWith("フォルダ1\") Then
            entry.Delete()

            Console.WriteLine("deleted '{0}'", entry.FullName)
          End If
        Next
      End Using
    End Using
  End Sub
End Class
実行結果
deleted 'フォルダ1\a.txt'
deleted 'フォルダ1\b.txt'
deleted 'フォルダ1\c.txt'

ファイル属性・パーミッションの指定 (ExternalAttributes)

ZipArchiveEntry.ExternalAttributesプロパティを参照することにより、ファイル属性・パーミッションを設定・取得することができる。 アーカイブする際にファイル属性を保存したい場合、逆に展開時にファイル属性を復元したい場合に、この属性を設定・取得する。 ExternalAttributesプロパティは、.NET Framework 4.7.2以降で使用することができる。

ExternalAttributesプロパティの値は、ZIPファイルフォーマットにおけるexternal file attributesの値としてマッピングされる。 ExternalAttributesプロパティの値のうち、下位16ビットはMS-DOSのディレクトリ属性(ファイル属性、FileAttributes列挙体相当)、上位16ビットはUnixのファイルパーミッションにマッピングされると記載されている。

ZipArchive.CreateEntryメソッドZipFileExtensions.CreateEntryFromFileメソッドで作成したZipArchiveEntryでは、ExternalAttributesプロパティの初期値は0となる(ファイルの属性・パーミッションは自動的に反映・マッピングされない)。 そのため、ExternalAttributesプロパティを特に指定せずに作成したZIPアーカイブをUnix系のファイルシステムで展開すると、パーミッションは000になり、読み取りすらできないファイルとして展開されてしまう。

例えば、§.ZIPアーカイブの作成のコードで作成したファイルを展開すると、パーミッションは次のように000なる。

ExternalAttributesプロパティを指定せずに作成したZIPアーカイブのファイルパーミッション
$ unzip ../sample.zip 
Archive:  ../sample.zip
  inflating: サンプル.txt        
  inflating: sample.txt              
  inflating: フォルダ1/a.txt     
  inflating: フォルダ1/c.txt     
  inflating: フォルダ1/b.txt     

$ tree -p
.
├── [----------]  sample.txt
├── [----------]  サンプル.txt
└── [drwxrwxr-x]  フォルダ1
    ├── [----------]  a.txt
    ├── [----------]  b.txt
    └── [----------]  c.txt

1 directory, 5 files

ExternalAttributesプロパティにUnixファイルパーミッションを指定してZIPアーカイブを作成する場合、次のようにExternalAttributesプロパティの上位16ビットにファイルパーミッションを指定する。

Unixファイルパーミッションを指定してZIPアーカイブを作成する .NET Framework 4.7.2
using System;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // ZIPアーカイブsample.zipを作成
    using (var archive = ZipFile.Open("sample.zip", ZipArchiveMode.Create)) {
      // ファイル"a.txt"をエントリ名"a.txt"でZipArchiveに追加
      var entry = archive.CreateEntryFromFile("a.txt", "a.txt");

      // Unixファイルパーミッションを'644' (-rw-r--r--)に設定する
      entry.ExternalAttributes = Convert.ToInt32("644", 8) << 16;
    }
  }
}
Unixファイルパーミッションを指定してZIPアーカイブを作成する .NET Framework 4.7.2
Imports System
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' ZIPアーカイブsample.zipを作成
    Using archive As ZipArchive = ZipFile.Open("sample.zip", ZipArchiveMode.Create)
      ' ファイル"a.txt"をエントリ名"a.txt"でZipArchiveに追加
      Dim entry As ZipArchiveEntry = archive.CreateEntryFromFile("a.txt", "a.txt")

      ' Unixファイルパーミッションを'644' (-rw-r--r--)に設定する
      entry.ExternalAttributes = &O644 << 16
    End Using
  End Sub
End Class
上記のコードで作成されるZIPアーカイブを展開して、パーミッションを表示した結果
$ unzip ../sample.zip 
Archive:  ../sample.zip
  inflating: a.txt                   

$ tree -p
.
└── [-rw-r--r--]  a.txt

エントリ名のエンコーディング (entryNameEncoding)

ZipArchiveコンストラクタ、あるいはZipFile.Openメソッドの引数entryNameEncodingを指定することで、ZIPアーカイブ内を展開・作成する際のエントリ名のエンコーディングを指定することができる。 ZipFile.OpenReadメソッドには引数entryNameEncodingが存在しないため、エンコーディングを指定する必要がある場合は、ZipFile.Openメソッドを使用する。

この値を指定しない場合、エントリ名はUTF-8であるものとしてエンコード/デコードされる。 ZIPアーカイブをUTF-8以外で作成する必要がある場合、UTF-8以外のエンコーディングで作成されたアーカイブを展開する場合などには、entryNameEncodingに対して目的のEncodingを明示的に指定する必要がある。

エントリ名のエンコーディングにShift_JISを使用してZIPアーカイブを作成する
using System;
using System.IO.Compression;
using System.Text;

class Sample {
  static void Main()
  {
    // `dotnet add test.csproj package System.Text.Encoding.CodePages`でパッケージ参照を追加する必要あり
    var shift_jis = CodePagesEncodingProvider.Instance.GetEncoding("Shift_JIS");

    // entryNameEncodingにShift_JISを指定してZIPアーカイブsample.zipを作成
    using (var archive = ZipFile.Open("sample.zip", ZipArchiveMode.Create, entryNameEncoding: shift_jis)) {
      // ファイル"テスト.txt"をエントリ名"テスト.txt"でZipArchiveに追加
      var entry = archive.CreateEntryFromFile("テスト.txt", "テスト.txt");
    }
  }
}
エントリ名のエンコーディングにShift_JISを使用してZIPアーカイブを作成する
Imports System
Imports System.IO.Compression
Imports System.Text

Class Sample
  Shared Sub Main()
    ' `dotnet add test.vbproj package System.Text.Encoding.CodePages`でパッケージ参照を追加する必要あり
    Dim shift_jis As Encoding = CodePagesEncodingProvider.Instance.GetEncoding("Shift_JIS")

    ' entryNameEncodingにShift_JISを指定してZIPアーカイブsample.zipを作成
    Using archive As ZipArchive = ZipFile.Open("sample.zip", ZipArchiveMode.Create, entryNameEncoding := shift_jis)
      ' ファイル"テスト.txt"をエントリ名"テスト.txt"でZipArchiveに追加
      Dim entry As ZipArchiveEntry = archive.CreateEntryFromFile("テスト.txt", "テスト.txt")
    End Using
  End Sub
End Class
上記のコードで作成されるZIPアーカイブを、文字コードShift JISとして展開した結果
$ unzip -I sjis ../sample.zip
Archive:  ../sample.zip
  inflating: テスト.txt           
エントリ名のエンコーディングにShift_JISを使用してZIPアーカイブを展開する
using System;
using System.IO.Compression;
using System.Text;

class Sample {
  static void Main()
  {
    // `dotnet add test.csproj package System.Text.Encoding.CodePages`でパッケージ参照を追加する必要あり
    var shift_jis = CodePagesEncodingProvider.Instance.GetEncoding("Shift_JIS");

    // entryNameEncodingにShift_JISを指定してZIPアーカイブsample.zipを展開
    using (var archive = ZipFile.Open("sample.zip", ZipArchiveMode.Read, entryNameEncoding: shift_jis)) {
      // アーカイブ内のエントリ名を表示する
      foreach (var entry in archive.Entries) {
        Console.WriteLine(entry.Name);
      }
    }
  }
}
エントリ名のエンコーディングにShift_JISを使用してZIPアーカイブを展開する
Imports System
Imports System.IO.Compression
Imports System.Text

Class Sample
  Shared Sub Main()
    ' `dotnet add test.vbproj package System.Text.Encoding.CodePages`でパッケージ参照を追加する必要あり
    Dim shift_jis As Encoding = CodePagesEncodingProvider.Instance.GetEncoding("Shift_JIS")

    ' entryNameEncodingにShift_JISを指定してZIPアーカイブsample.zipを作成
    Using archive As ZipArchive = ZipFile.Open("sample.zip", ZipArchiveMode.Read, entryNameEncoding := shift_jis)
      ' アーカイブ内のエントリ名を表示する
      For Each entry As ZipArchiveEntry In archive.Entries
        Console.WriteLine(entry.Name)
      Next
    End Using
  End Sub
End Class

ZipArchiveMode.Updateを指定してZIPアーカイブの更新を行う場合、展開と圧縮でどちらも同じentryNameEncodingが使用されることになる。 そのため、ZIPアーカイブの更新ではエンコーディングの変更を行うことはできない。 エンコーディングの変更を行う場合は、一旦アーカイブを展開したのち、目的のエンコーディングを指定した別のアーカイブとして再圧縮する必要がある。

ZipFileクラス

ZipFileクラスには、ZIPアーカイブを扱う際によく行われる操作に対応するユーティリティメソッドが用意されている。 フォルダをまるごとZIPアーカイブとして圧縮したり、逆にZIPアーカイブをフォルダに展開するといった単純な操作は、ZipFileクラスのメソッドを呼び出すだけで行うことができる。

アセンブリSystem.IO.Compression.dllに含まれるZipArchiveクラスに対して、ZipFileクラスはSystem.IO.Compression.FileSystem.dllに含まれるので、使う場合にはこのアセンブリも参照に含める必要がある。

ZIPアーカイブの展開

ZipFile.ExtractToDirectoryメソッドを使うと、ZIPアーカイブを任意のフォルダに展開できる。

// csc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.cs
using System;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // sample.zipを展開し、展開したファイルをフォルダ"extract"以下に格納する
    ZipFile.ExtractToDirectory("sample.zip", "extract");
  }
}
' vbc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.vb
Imports System
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' sample.zipを展開し、展開したファイルをフォルダ"extract"以下に格納する
    ZipFile.ExtractToDirectory("sample.zip", "extract")
  End Sub
End Class

上記の例と同等の動作をするコードをZipArchiveクラスを使って記述した例

ZIPアーカイブの作成

ZipFile.CreateFromDirectoryメソッドを使うと、指定したフォルダの内容を圧縮してZIPアーカイブを作成できる。

引数includeBaseDirectoryにtrueを指定した場合、アーカイブされるファイルにはディレクトリ名も含まれる(フォルダ構造が維持される)。 falseを指定した場合は、ファイルのみとなる(フラットな構造でアーカイブされる)。 falseを指定した場合、既に同名のファイルがアーカイブ内に存在する場合はIOExceptionがスローされる。

// csc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.cs
using System;
using System.IO;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // ZIPアーカイブとして圧縮したいファイルが存在するディレクトリ
    const string sourceDirectory = "source";

    // ZIPアーカイブとして圧縮するテスト用のファイルを用意する
    CreateTestFiles(sourceDirectory);

    // フォルダ"source"のすべてのファイルを、フォルダ構造を維持したままsample.zipとして圧縮する
    ZipFile.CreateFromDirectory(sourceDirectory, "sample.zip", CompressionLevel.Fastest, true);
  }

  private static void CreateTestFiles(string dir)
  {
    if (Directory.Exists(dir))
      Directory.Delete(dir, true);

    Directory.CreateDirectory(dir);

    File.WriteAllText(Path.Combine(dir, "sample.txt"), "Hello, world!");
    File.WriteAllText(Path.Combine(dir, "サンプル.txt"), "こんにちは世界");

    var subdir = Path.Combine(dir, "フォルダ1");

    Directory.CreateDirectory(subdir);

    File.WriteAllText(Path.Combine(subdir, "a.txt"), new string('a', 1024));
    File.WriteAllText(Path.Combine(subdir, "b.txt"), new string('b', 1024));
    File.WriteAllText(Path.Combine(subdir, "c.txt"), new string('c', 1024));
  }
}
' vbc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.vb
Imports System
Imports System.IO
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' ZIPアーカイブとして圧縮したいファイルが存在するディレクトリ
    Const sourceDirectory As String = "source"

    ' ZIPアーカイブとして圧縮するテスト用のファイルを用意する
    CreateTestFiles(sourceDirectory)

    ' フォルダ"source"のすべてのファイルを、フォルダ構造を維持したままsample.zipとして圧縮する
    ZipFile.CreateFromDirectory(sourceDirectory, "sample.zip", CompressionLevel.Fastest, True)
  End Sub

  Private Shared Sub CreateTestFiles(ByVal dir As String)
    If Directory.Exists(dir) Then Directory.Delete(dir, True)

    Directory.CreateDirectory(dir)

    File.WriteAllText(Path.Combine(dir, "sample.txt"), "Hello, world!")
    File.WriteAllText(Path.Combine(dir, "サンプル.txt"), "こんにちは世界")

    Dim subdir As String = Path.Combine(dir, "フォルダ1")

    Directory.CreateDirectory(subdir)

    File.WriteAllText(Path.Combine(subdir, "a.txt"), New String("a"c, 1024))
    File.WriteAllText(Path.Combine(subdir, "b.txt"), New String("b"c, 1024))
    File.WriteAllText(Path.Combine(subdir, "c.txt"), New String("c"c, 1024))
  End Sub
End Class

上記の例と同等の動作をするコードをZipArchiveクラスを使って記述した例

拡張メソッド

System.IO.Compression.FileSystem.dllを参照に追加すると、ZipArchiveクラスおよびZipArchiveEntryクラスに拡張メソッドが追加される。 拡張メソッドを使うことにより、ZIPアーカイブの操作をよりシンプルに記述できる。

ExtractToDirectory

ExtractToDirectoryメソッドはZipArchiveクラスに対して追加される拡張メソッドで、ZipArchiveに含まれるすべてのファイルを指定したフォルダに展開して保存する。  従って、ExtractToDirectoryはZipFile.ExtractToDirectoryと同等の動作を行うメソッドとなる。

// csc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.cs
using System;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // sample.zipを開いてZIPアーカイブを展開するためのZipArchiveを作成
    using (var archive = ZipFile.OpenRead("sample.zip")) {
      // ZipArchiveからファイルを展開してフォルダ"extract"以下に格納する
      archive.ExtractToDirectory("extract");
    }
  }
}
' vbc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.vb
Imports System
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' sample.zipを開いてZIPアーカイブを展開するためのZipArchiveを作成
    Using archive As ZipArchive = ZipFile.OpenRead("sample.zip")
      ' ZipArchiveからファイルを展開してフォルダ"extract"以下に格納する
      archive.ExtractToDirectory("extract")
    End Using
  End Sub
End Class

ExtractToFile

ExtractToFileメソッドはZipArchiveEntryクラス対して追加される拡張メソッドで、ZipArchiveEntryの内容を展開して指定したファイル名で保存することができる。 このメソッドは展開する際のファイル名を変更したり、個別に指定したりしたい場合などに使うことができる。

// csc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.cs
using System;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // sample.zipを開いてZIPアーカイブを展開するためのZipArchiveを作成
    using (var archive = ZipFile.OpenRead("sample.zip")) {
      // ZIPアーカイブ内の全エントリを列挙
      for (int index = 0; index < archive.Entries.Count; index++) {
        var entry = archive.Entries[index];

        // エントリを展開し、インデックスをファイル名に使用して保存
        entry.ExtractToFile(string.Format("{0}.tmp", index));
      }
    }
  }
}
' vbc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.vb
Imports System
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' sample.zipを開いてZIPアーカイブを展開するためのZipArchiveを作成
    Using archive As ZipArchive = ZipFile.OpenRead("sample.zip")
      ' ZIPアーカイブ内の全エントリを列挙
      For index As Integer = 0 To archive.Entries.Count - 1
        Dim entry As ZipArchiveEntry = archive.Entries(index)

        ' エントリを展開し、インデックスをファイル名に使用して保存
        entry.ExtractToFile(String.Format("{0}.tmp", index))
      Next
    End Using
  End Sub
End Class

CreateEntryFromFile

CreateEntryFromFileメソッドはZipArchiveクラス対して追加される拡張メソッドで、指定したファイルの内容でZipArchiveEntryを作成し、ZipArchiveに追加することができる。

// csc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.cs
using System;
using System.IO.Compression;

class Sample {
  static void Main()
  {
    // ZIPアーカイブを作成してsample.zipに書き込むためのZipArchiveを作成
    using (var archive = ZipFile.Open("sample.zip", ZipArchiveMode.Update)) {
      // ファイル"a.txt"をエントリ名"1"でZipArchiveに追加
      var entry = archive.CreateEntryFromFile("a.txt", "1");

      // 追加したエントリの変更日時に別の値を設定
      // (ZipArchiveMode.CreateでZipArchiveを作成した場合は、プロパティを変更するとIOExceptionとなる)
      entry.LastWriteTime = new DateTimeOffset(2000, 1, 23, 4, 56, 7, new TimeSpan(8, 0, 0));

      // さらに別のファイルをZipArchiveに追加
      archive.CreateEntryFromFile("b.txt", "2");
      archive.CreateEntryFromFile("c.txt", "3");
    }
  }
}
' vbc /r:System.IO.Compression.dll /r:System.IO.Compression.FileSystem.dll Sample.vb
Imports System
Imports System.IO.Compression

Class Sample
  Shared Sub Main()
    ' ZIPアーカイブを作成してsample.zipに書き込むためのZipArchiveを作成
    Using archive As ZipArchive = ZipFile.Open("sample.zip", ZipArchiveMode.Update)
      ' ファイル"a.txt"をエントリ名"1"でZipArchiveに追加
      Dim entry As ZipArchiveEntry = archive.CreateEntryFromFile("a.txt", "1")

      ' 追加したエントリの変更日時に別の値を設定
      ' (ZipArchiveMode.CreateでZipArchiveを作成した場合は、プロパティを変更するとIOExceptionとなる)
      entry.LastWriteTime = New DateTimeOffset(2000, 1, 23, 4, 56, 7, New TimeSpan(8, 0, 0))

      ' さらに別のファイルをZipArchiveに追加
      archive.CreateEntryFromFile("b.txt", "2")
      archive.CreateEntryFromFile("c.txt", "3")
    End Using
  End Sub
End Class