C# ZipArchiveの使い方完全ガイド|ZIP作成・解凍・追記・文字化け対策まで

はじめに

C#でファイル圧縮や解凍を行う際に便利なのがZipArchiveです。ZIPファイルの作成・解凍・追記・削除などが手軽に行え、さらに文字化け対策や大容量ファイル処理にも対応しています。本記事では、C# ZipArchiveの基本から応用まで、サンプルコード付きでわかりやすく解説します。

1. C#のZipArchiveとは?できることと使いどころ

ZipArchiveは、.NETの標準ライブラリで提供されるZIP操作用クラスです。単一ファイルやフォルダを圧縮・解凍したり、既存のZIPにファイルを追加したりと柔軟に扱えます。

1-1. ZipArchive・ZipFile・ZipArchiveEntryの違い

  • ZipArchive:ZIP全体を扱うクラス。エントリ単位での操作が可能

  • ZipFile:フォルダ丸ごと圧縮や一括解凍が簡単にできるユーティリティクラス

  • ZipArchiveEntry:ZIP内の個別ファイルやディレクトリを表すオブジェクト

用途に応じて使い分けると効率的です。

1-2. ZipArchiveでできる操作一覧:作成・解凍・追記・削除・一覧取得

ZipArchiveでは以下の操作が可能です。

  • ZIPファイル作成(新規・既存への追記)

  • ZIPファイル解凍

  • 個別ファイルの追加・更新・削除

  • エントリ一覧の取得や検索

  • メモリ上でのZIP操作

1-3. ZipFileだけで十分なケースとZipArchiveを使うべきケース

  • ZipFileで十分:フォルダ丸ごと圧縮・解凍したい場合

  • ZipArchiveを推奨:特定ファイルだけ操作したい、追記・更新・削除が必要な場合、文字コードを指定したい場合

1-4. .NET Framework/.NET Core/.NET 5以降での対応範囲

  • .NET Framework 4.5以降、System.IO.Compressionで使用可能

  • .NET Core 2.0以降、ZipArchiveは標準サポート

  • .NET 5以降ではパフォーマンス改善やエンコーディング指定がより柔軟

1-5. この記事で扱うサンプルコードの前提環境

  • .NET 6/C# 10を想定

  • Visual Studio 2022 または VS Code

  • 必要に応じて System.IO.CompressionSystem.IO.Compression.FileSystem を参照

2. C#でZipArchiveを使うための準備

2-1. 必要な名前空間:System.IO.Compression

C#
using System.IO;
using System.IO.Compression;

2-2. 必要な参照・NuGetパッケージ

  • .NET Frameworkの場合、System.IO.Compression.FileSystemの参照が必要

  • .NET Core/.NET 5以降は標準で利用可能

2-3. ZipArchiveModeの違い:Create・Read・Update

  • Create:新規ZIPを作成

  • Read:既存ZIPを読み込み

  • Update:既存ZIPに追記・更新・削除

2-4. using文でリソースを正しく解放する

C#
using (var archive = ZipFile.Open(zipPath, ZipArchiveMode.Update))
{
// 操作
}

usingを使うことでファイルストリームを安全に解放できます。

2-5. ファイルパス指定時の注意点

  • 相対パス/絶対パスの混在に注意

  • 長いパスや特殊文字がある場合は Path.GetFullPath を利用

3. C#でZIPファイルを作成する方法

3-1. フォルダを丸ごとZIP化する:ZipFile.CreateFromDirectory

C#
ZipFile.CreateFromDirectory(@"C:\SourceFolder", @"C:\Archive.zip", CompressionLevel.Optimal, includeBaseDirectory: true);

3-2. ZipArchiveでファイルを1つずつ追加してZIPを作成する

C#
using (var archive = ZipFile.Open("Archive.zip", ZipArchiveMode.Create))
{
archive.CreateEntryFromFile("file1.txt", "file1.txt");
archive.CreateEntryFromFile("file2.txt", "file2.txt");
}

3-3. サブフォルダ構造を保ったままZIP化する

エントリ名にサブフォルダを含めることで階層を維持できます。

C#
archive.CreateEntryFromFile(@"C:\Folder\file.txt", @"SubFolder/file.txt");

3-4. 圧縮レベルを指定する:Optimal・Fastest・NoCompression

C#
archive.CreateEntryFromFile("file.txt", "file.txt", CompressionLevel.Fastest);

3-5. メモリ上でZIPを作成する方法

C#
using (var ms = new MemoryStream())
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, true))
{
archive.CreateEntryFromFile("file.txt", "file.txt");
}

3-6. 既存ZIPと同名ファイルがある場合のエラー対策

  • 追加前に archive.GetEntry("file.txt") で存在チェック

  • 存在する場合は削除または名前変更

4. C#でZIPファイルを解凍する方法

4-1. ZIPをフォルダへ一括解凍する:ZipFile.ExtractToDirectory

C#
ZipFile.ExtractToDirectory("Archive.zip", @"C:\Extracted");

4-2. ZipArchiveで中身を確認しながら解凍する

C#
using (var archive = ZipFile.OpenRead("Archive.zip"))
{
foreach (var entry in archive.Entries)
{
Console.WriteLine(entry.FullName);
entry.ExtractToFile(Path.Combine(@"C:\Extracted", entry.FullName));
}
}

4-3. 特定のファイルだけを取り出す

C#
var entry = archive.GetEntry("file.txt");
entry?.ExtractToFile(@"C:\Extracted\file.txt");

4-4. 解凍先に同名ファイルがある場合の上書き対応

  • ExtractToFile の第2引数で上書き可能

C#
entry.ExtractToFile(destPath, overwrite: true);

4-5. ディレクトリ付きエントリを安全に展開する

  • フォルダが存在しない場合は Directory.CreateDirectory を使用してから展開

4-6. 不正なZIPや破損ZIPの例外処理

  • InvalidDataExceptionIOException をキャッチして処理

5. ZIPファイルにファイルを追記・更新・削除する方法

5-1. ZipArchiveMode.Updateで既存ZIPを開く

C#
using (var archive = ZipFile.Open("Archive.zip", ZipArchiveMode.Update))
{
// 追記・更新・削除操作
}

5-2. ZIPに新しいファイルを追加する

C#
archive.CreateEntryFromFile("newfile.txt", "newfile.txt");

5-3. ZIP内の既存ファイルを更新する

C#
var entry = archive.GetEntry("file.txt");
entry.Delete();
archive.CreateEntryFromFile("file.txt", "file.txt");

5-4. ZIP内のファイルを削除する

C#
var entry = archive.GetEntry("deletefile.txt");
entry?.Delete();

5-5. 同名エントリを追加しないためのチェック方法

C#
if (archive.GetEntry("file.txt") == null)
{
archive.CreateEntryFromFile("file.txt", "file.txt");
}

5-6. Updateモード使用時の注意点と制限

  • エントリ操作中に同時に別のエントリを開くと例外が発生する場合あり

6. ZIP内のファイル一覧を取得・検索する方法

6-1. ZipArchive.Entriesでエントリ一覧を取得する

C#
foreach (var entry in archive.Entries)
{
Console.WriteLine(entry.FullName);
}

6-2. ファイル名・拡張子でZIP内を検索する

C#
var txtFiles = archive.Entries.Where(e => e.FullName.EndsWith(".txt"));

6-3. フォルダ階層を判定する

  • entry.FullName/ が含まれていればサブフォルダの可能性あり

6-4. ファイルサイズ・更新日時を取得する

C#
Console.WriteLine(entry.Length);
Console.WriteLine(entry.LastWriteTime);

6-5. ZIP内ファイルを読み込んで文字列として扱う

C#
using (var reader = new StreamReader(entry.Open()))
{
string content = reader.ReadToEnd();
}

6-6. ZIPの中身を展開せずに処理する実装例

  • メモリ上でStreamを読み込み解析可能

7. C# ZipArchiveの文字化け対策

7-1. ZIP解凍時に日本語ファイル名が文字化けする原因

  • ZIP標準はUTF-8を推奨

  • Windows標準ZIPはShift_JISを使用

7-2. UTF-8とShift_JISの違いを理解する

  • UTF-8:国際的に標準化、文字化けが少ない

  • Shift_JIS:日本語Windows環境でのみ安全

7-3. ZipFile.OpenでEncodingを指定する方法

C#
var archive = ZipFile.Open("Archive.zip", ZipArchiveMode.Read, Encoding.GetEncoding("shift_jis"));

7-4. ExtractToDirectoryでEncodingを指定する方法

C#
archive.ExtractToDirectory(@"C:\Extracted", Encoding.UTF8);

7-5. .NET 9以降のZipArchiveEntryエンコーディング仕様変更

  • UTF-8がデフォルトになり、日本語文字化け対策が簡単になった

7-6. Windows標準ZIP・7-Zip・他システム連携時の注意点

  • 他環境とやり取りする場合はUTF-8で統一すると安全

7-7. 文字化けを防ぐ実用サンプルコード

C#
using (var archive = ZipFile.Open("Archive.zip", ZipArchiveMode.Read, Encoding.UTF8))
{
archive.ExtractToDirectory(@"C:\Extracted");
}

8. ZipArchiveでよくあるエラーと解決策

8-1. 「The archive entry was compressed using an unsupported compression method」の対処法

  • 圧縮方式が非対応の可能性

  • SharpZipLibなど代替ライブラリで対応

8-2. 「End of Central Directory record could not be found」の原因

  • ZIPが破損している、または部分的にしか取得できていない

8-3. 「Cannot access a closed Stream」の解決策

  • Streamのライフタイム管理を using 文で行う

8-4. 「Entries cannot be created while previously created entries are still open」の対処法

  • 同時に複数のエントリを開かない

  • Streamを閉じてから次を作成

8-5. ファイルロック・アクセス権限エラーへの対応

  • ファイル使用中や権限不足のチェック

  • 必要に応じて FileShare.ReadWrite を指定

8-6. パスが長すぎる場合の対策

  • \\?\ プレフィックスを使用して長いパスに対応

8-7. Zip Slip脆弱性を防ぐ安全な解凍方法

  • エントリ名の正規化と解凍先チェックを行う

C#
string fullPath = Path.GetFullPath(Path.Combine(destDir, entry.FullName));
if (!fullPath.StartsWith(destDir)) throw new IOException("不正なパス");
entry.ExtractToFile(fullPath);

9. 実務で使えるZipArchiveの応用パターン

9-1. 複数ファイルをダウンロード用ZIPとして返す

  • Webアプリでファイルをまとめて配信可能

9-2. ASP.NET CoreでZIPを生成してレスポンスする

C#
using var memory = new MemoryStream();
using (var archive = new ZipArchive(memory, ZipArchiveMode.Create, true))
{
archive.CreateEntryFromFile("file.txt", "file.txt");
}
memory.Position = 0;
return File(memory, "application/zip", "Download.zip");

9-3. パスワード付きZIPはZipArchiveで作成できる?

  • 標準では非対応

  • SharpZipLib7-Zipライブラリを併用

9-4. 大容量ファイルを扱うときのメモリ節約

  • Streamを直接ZIPに書き込み、メモリに全体を展開しない

9-5. 一時ファイルを使わずStreamでZIP処理する

  • メモリ上でのZIP作成により高速かつ安全

9-6. ログ・帳票・バックアップファイルのZIP化

  • 定期処理やバッチ処理に最適

10. ZipArchiveを使うときのベストプラクティス

10-1. using/Disposeでリソースリークを防ぐ

  • StreamやZipArchiveは必ず閉じる

10-2. 例外処理を入れるべきポイント

  • ファイル操作時、解凍時、追記・削除時

10-3. 相対パスと絶対パスを安全に扱う

  • Path.GetFullPathPath.Combine を利用

10-4. エンコーディング指定の方針を決める

  • UTF-8をデフォルトにすると文字化けリスクが低減

10-5. 大量ファイル・大容量ZIPでのパフォーマンス対策

  • Stream単位で処理

  • メモリに展開せずに書き込み

10-6. SharpZipLibや7-Zipを検討すべきケース

  • パスワード付きZIP、特殊圧縮方式、大容量ZIPなど

11. C# ZipArchiveのよくある質問

11-1. ZipArchiveとZipFileはどちらを使えばいい?

  • 単純圧縮・解凍ならZipFile

  • 個別操作・追記・文字コード指定ならZipArchive

11-2. C#でパスワード付きZIPは作れる?

  • 標準では不可

  • SharpZipLibや7-Zipライブラリを使用

11-3. ZIPファイルに追記できない原因は?

  • Updateモードで開いていない

  • エントリが開いたままになっている

11-4. 日本語ファイル名が文字化けする場合はどうすればいい?

  • UTF-8指定またはShift_JIS指定で解凍

11-5. ZIP内のファイルを展開せずに読み込める?

  • ZipArchiveEntry.Open() でStreamを取得し、メモリ展開せずに処理可能

11-6. フォルダ構造を維持してZIP化するには?

  • エントリ名にサブフォルダを含める

11-7. .NET FrameworkでもZipArchiveは使える?

  • .NET Framework 4.5以降で使用可能

  • 参照設定に注意

まとめ

C#のZipArchiveは、ZIPファイルを自在に操作できる強力なツールです。フォルダ丸ごとの圧縮、個別ファイルの追加・削除、文字化け対策、大容量ファイル処理など、実務で役立つ操作をカバーしています。基本の使い方を押さえつつ、StreamやUpdateモードを活用すれば、より柔軟で安全なZIP処理が実現できます。