C#のZipFileでZIP圧縮・解凍する方法|基本コードからエラー対策まで解説

はじめに

C#でZIPファイルを作成・解凍したい場合、標準ライブラリのZipFileクラスを使うと、外部ライブラリを追加しなくても比較的シンプルに実装できます。たとえば、フォルダ全体をZIP圧縮する、ZIPファイルを指定フォルダへ解凍する、ZIP内のファイル一覧を取得する、といった処理をC#だけで行えます。

この記事では、csharp zipfileや「C# ZipFile」で検索している方に向けて、ZipFileの基本的な使い方から、個別ファイルの操作、よくあるエラー、安全に解凍するためのZip Slip対策まで解説します。

1. C#のZipFileとは?ZIP圧縮・解凍に使う基本クラス

ZipFileは、C#でZIPアーカイブを扱うためのクラスです。名前空間はSystem.IO.Compressionで、フォルダをZIP化するCreateFromDirectoryや、ZIPをフォルダに展開するExtractToDirectoryなどのメソッドが用意されています。Microsoft公式ドキュメントでも、ZipFileはフォルダーの内容をZIPアーカイブに圧縮し、その内容を新しいフォルダーへ抽出する用途で紹介されています。Microsoft Learn+1

1-1. ZipFileでできること

ZipFileを使うと、主に次のような処理ができます。

C#
using System.IO.Compression;

// フォルダをZIP圧縮
ZipFile.CreateFromDirectory(@"C:\work\source", @"C:\work\backup.zip");

// ZIPを解凍
ZipFile.ExtractToDirectory(@"C:\work\backup.zip", @"C:\work\extract");

代表的な用途は、フォルダ単位のバックアップ、ログファイルの圧縮、アップロードされたZIPファイルの展開、複数ファイルの一括ダウンロードなどです。

1-2. ZipFileとZipArchiveの違い

ZipFileは、フォルダ全体の圧縮やZIP全体の解凍を簡単に行うための高レベルなクラスです。一方、ZipArchiveはZIPファイル内のエントリを1つずつ操作するためのクラスです。

Microsoft公式ドキュメントでは、ディレクトリからZIPを作成する場合はZipFile.CreateFromDirectory、ZIP全体をディレクトリへ抽出する場合はZipFile.ExtractToDirectory、既存ZIPに新しいファイルを追加する場合はZipArchive.CreateEntry、ZIP内のファイル一覧を取得する場合はZipArchive.Entriesを使う、と整理されています。Microsoft Learn

つまり、ざっくり分けると次のようになります。

やりたいこと主に使うクラス
フォルダ全体をZIP化したいZipFile
ZIP全体を解凍したいZipFile
ZIP内のファイル一覧を見たいZipArchive
ZIPにファイルを追加したいZipArchive
ZIP内の一部だけ解凍したいZipArchiveEntry

1-3. ZipFileを使うメリット・注意点

ZipFileのメリットは、コード量が少なく、標準機能だけでZIP圧縮・解凍を実装できる点です。単純なバックアップ処理や一時ファイルの圧縮であれば、数行のコードで十分です。

一方で、注意点もあります。ZipFileだけではパスワード付きZIPの作成や解凍には対応していません。また、既存ファイルの上書き、文字コード、解凍時のパス検証などは、実装時に意識する必要があります。

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

2-1. 必要な名前空間

C#でZipFileを使うには、基本的に次の名前空間を追加します。

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

ZIP内の日本語ファイル名を扱うためにエンコーディングを指定する場合は、次の名前空間も使います。

C#
using System.Text;

2-2. 対応している.NETのバージョン

ZipFileは、.NET Frameworkや.NETの各バージョンで利用できます。ただし、プロジェクトの種類やターゲットフレームワークによって参照設定が必要になる場合があります。

特に.NET FrameworkアプリでZipFileを使う場合は、System.IO.Compression.FileSystemアセンブリへの参照追加が必要です。これはMicrosoft公式ドキュメントにも明記されています。Microsoft Learn+1

2-3. System.IO.Compression.FileSystemの参照方法

.NET FrameworkのプロジェクトでZipFileが見つからない場合は、Visual Studioで参照を追加します。

手順は次のとおりです。

  1. プロジェクトを右クリック

  2. 「参照の追加」を選択

  3. System.IO.Compression

  4. System.IO.Compression.FileSystem

  5. 必要に応じてSystem.IO.Compression.ZipFileパッケージを追加

.NET Core、.NET 5以降、.NET 6、.NET 8などのSDKスタイルのプロジェクトでは、基本的にusing System.IO.Compression;を追加するだけで使えるケースが多いです。

2-4. サンプルで使用するフォルダ構成

この記事では、次のようなフォルダ構成を例にします。

C:\zip-sample
├─ source
│ ├─ sample1.txt
│ ├─ sample2.txt
│ └─ sub
│ └─ sample3.txt
├─ backup.zip
└─ extract

圧縮元フォルダはsource、作成するZIPファイルはbackup.zip、解凍先フォルダはextractとして説明します。

3. ZipFileでフォルダをZIP圧縮する方法

3-1. CreateFromDirectoryの基本コード

フォルダ全体をZIP圧縮するには、ZipFile.CreateFromDirectoryを使います。

C#
using System.IO.Compression;

string sourceDirectory = @"C:\zip-sample\source";
string zipPath = @"C:\zip-sample\backup.zip";

ZipFile.CreateFromDirectory(sourceDirectory, zipPath);

Console.WriteLine("ZIPファイルを作成しました。");

このコードを実行すると、sourceフォルダ内のファイルとサブフォルダがbackup.zipとして圧縮されます。CreateFromDirectoryは、指定したディレクトリのファイルとディレクトリを含むZIPアーカイブを作成するメソッドです。Microsoft Learn+1

3-2. 圧縮レベルを指定する方法

圧縮レベルを指定したい場合は、CompressionLevelを使います。

C#
using System.IO.Compression;

string sourceDirectory = @"C:\zip-sample\source";
string zipPath = @"C:\zip-sample\backup.zip";

ZipFile.CreateFromDirectory(
sourceDirectory,
zipPath,
CompressionLevel.Optimal,
includeBaseDirectory: false
);

CompressionLevelには、主に次の値があります。

説明
Optimal圧縮率と速度のバランスを重視
Fastest圧縮速度を重視
NoCompression圧縮せず格納

includeBaseDirectorytrueにすると、圧縮元フォルダ自体を含めてZIP化します。falseにすると、フォルダの中身だけがZIPに入ります。

3-3. 既存のZIPファイルがある場合の注意点

CreateFromDirectoryで指定したZIPファイルがすでに存在する場合、基本的には例外が発生します。上書きしたい場合は、事前に既存ファイルを削除してから作成します。

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

string sourceDirectory = @"C:\zip-sample\source";
string zipPath = @"C:\zip-sample\backup.zip";

if (File.Exists(zipPath))
{
File.Delete(zipPath);
}

ZipFile.CreateFromDirectory(sourceDirectory, zipPath);

既存ファイルを削除する場合は、本当に削除してよいファイルかを確認してから実行しましょう。特にバックアップ処理では、古いZIPをリネームして退避する方法もおすすめです。

C#
if (File.Exists(zipPath))
{
string oldZipPath = Path.ChangeExtension(zipPath, ".old.zip");
File.Move(zipPath, oldZipPath, overwrite: true);
}

3-4. 空フォルダやサブフォルダを含めて圧縮する方法

CreateFromDirectoryは、圧縮元フォルダ配下のサブフォルダも含めてZIP化します。サブフォルダを含めたい場合でも、特別な再帰処理を書く必要はありません。

C#
ZipFile.CreateFromDirectory(
@"C:\zip-sample\source",
@"C:\zip-sample\backup.zip",
CompressionLevel.Optimal,
includeBaseDirectory: true
);

ただし、アプリケーションによっては空フォルダの扱いに注意が必要です。ZIPを作成する目的が「ファイルのバックアップ」なのか「フォルダ構造の保存」なのかによって、事前にテストしておくと安心です。

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

4-1. ExtractToDirectoryの基本コード

ZIPファイルを解凍するには、ZipFile.ExtractToDirectoryを使います。

C#
using System.IO.Compression;

string zipPath = @"C:\zip-sample\backup.zip";
string extractPath = @"C:\zip-sample\extract";

ZipFile.ExtractToDirectory(zipPath, extractPath);

Console.WriteLine("ZIPファイルを解凍しました。");

ExtractToDirectoryは、指定したZIPアーカイブ内のすべてのファイルをファイルシステム上のディレクトリへ抽出するメソッドです。Microsoft Learn

4-2. 解凍先フォルダを指定する方法

解凍先フォルダが存在しない場合、ExtractToDirectoryは必要なディレクトリを作成して抽出します。Microsoft公式ドキュメントでも、抽出先ディレクトリと、ZIP内の階層に対応するサブディレクトリが作成されると説明されています。Microsoft Learn

C#
string zipPath = @"C:\zip-sample\backup.zip";
string extractPath = @"C:\zip-sample\extract\2026";

ZipFile.ExtractToDirectory(zipPath, extractPath);

業務アプリでは、日付ごとに解凍先を分けると管理しやすくなります。

C#
string extractPath = Path.Combine(
@"C:\zip-sample\extract",
DateTime.Now.ToString("yyyyMMdd_HHmmss")
);

ZipFile.ExtractToDirectory(zipPath, extractPath);

4-3. 既存ファイルを上書きして解凍する方法

既存ファイルを上書きして解凍したい場合は、overwriteFiles引数を使えるオーバーロードを利用します。

C#
using System.IO.Compression;

string zipPath = @"C:\zip-sample\backup.zip";
string extractPath = @"C:\zip-sample\extract";

ZipFile.ExtractToDirectory(
zipPath,
extractPath,
overwriteFiles: true
);

ExtractToDirectoryには、必要に応じて解凍先ディレクトリ内のファイルを上書きするかどうかを指定できるオーバーロードがあります。Microsoft Learn+1

古い.NET FrameworkなどでoverwriteFilesが使えない場合は、解凍先を事前に削除する方法があります。

C#
if (Directory.Exists(extractPath))
{
Directory.Delete(extractPath, recursive: true);
}

ZipFile.ExtractToDirectory(zipPath, extractPath);

ただし、この方法は解凍先フォルダ内の既存データをすべて削除するため、重要なファイルが混在しない場所に限定してください。

4-4. 日本語ファイル名を含むZIPを解凍する際の注意点

ZIP内の日本語ファイル名が文字化けする場合は、エンコーディングを指定して解凍します。

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

string zipPath = @"C:\zip-sample\japanese.zip";
string extractPath = @"C:\zip-sample\extract";

ZipFile.ExtractToDirectory(
zipPath,
extractPath,
Encoding.UTF8
);

古いツールで作成されたZIPでは、ファイル名がShift_JISで格納されていることがあります。その場合は、コードページを使えるようにした上でEncoding.GetEncoding("shift_jis")を指定します。.NET Coreや.NET 5以降では、追加のコードページを使うためにCodePagesEncodingProviderを登録する方法があります。Microsoft Learn+1

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

// NuGetで System.Text.Encoding.CodePages が必要な場合があります
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

ZipFile.ExtractToDirectory(
@"C:\zip-sample\japanese.zip",
@"C:\zip-sample\extract",
Encoding.GetEncoding("shift_jis")
);

文字化け対策では、「ZIPを作成した環境」と「解凍する環境」の文字コードを確認することが重要です。

5. ZipFileで個別ファイルを扱う方法

5-1. ZIP内のファイル一覧を取得する方法

ZIP内のファイル一覧を取得するには、ZipFile.OpenReadまたはZipFile.OpenZipArchiveを開き、Entriesを参照します。ZipArchive.Entriesは、ZIPアーカイブ内のエントリのコレクションを取得するプロパティです。Microsoft Learn

C#
using System.IO.Compression;

string zipPath = @"C:\zip-sample\backup.zip";

using ZipArchive archive = ZipFile.OpenRead(zipPath);

foreach (ZipArchiveEntry entry in archive.Entries)
{
Console.WriteLine(entry.FullName);
}

ファイルサイズや更新日時も取得できます。

C#
foreach (ZipArchiveEntry entry in archive.Entries)
{
Console.WriteLine($"{entry.FullName} / {entry.Length} bytes / {entry.LastWriteTime}");
}

5-2. ZIPにファイルを追加する方法

既存ZIPにファイルを追加する場合は、ZipFile.OpenZipArchiveMode.Updateを指定し、CreateEntryFromFileを使います。CreateEntryFromFileは、ファイルを圧縮してZIPアーカイブに追加する拡張メソッドです。Microsoft Learn+1

C#
using System.IO.Compression;

string zipPath = @"C:\zip-sample\backup.zip";
string fileToAdd = @"C:\zip-sample\add.txt";

using ZipArchive archive = ZipFile.Open(zipPath, ZipArchiveMode.Update);

archive.CreateEntryFromFile(
fileToAdd,
"add.txt",
CompressionLevel.Optimal
);

ZIP内のフォルダに追加したい場合は、エントリ名に相対パスを指定します。

C#
archive.CreateEntryFromFile(
fileToAdd,
"docs/add.txt",
CompressionLevel.Optimal
);

5-3. ZIP内のファイルを削除する方法

ZIP内のファイルを削除するには、ZipArchiveMode.Updateで開き、対象のエントリを取得してDeleteします。

C#
using System.IO.Compression;

string zipPath = @"C:\zip-sample\backup.zip";

using ZipArchive archive = ZipFile.Open(zipPath, ZipArchiveMode.Update);

ZipArchiveEntry? entry = archive.GetEntry("add.txt");

if (entry != null)
{
entry.Delete();
Console.WriteLine("ZIP内のファイルを削除しました。");
}

GetEntryでは、ZIP内の相対パスを指定します。docs/add.txtとして追加したファイルは、GetEntry("docs/add.txt")で取得します。

5-4. ZIP内の特定ファイルだけを解凍する方法

ZIP内の特定ファイルだけを解凍する場合は、ZipArchiveEntry.ExtractToFileを使います。ExtractToFileには、同名ファイルを上書きできるオーバーロードもあります。Microsoft Learn+1

C#
using System.IO.Compression;

string zipPath = @"C:\zip-sample\backup.zip";
string outputPath = @"C:\zip-sample\extract\sample1.txt";

using ZipArchive archive = ZipFile.OpenRead(zipPath);

ZipArchiveEntry? entry = archive.GetEntry("sample1.txt");

if (entry != null)
{
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
entry.ExtractToFile(outputPath, overwrite: true);
}

特定の拡張子だけを解凍することもできます。

C#
using ZipArchive archive = ZipFile.OpenRead(zipPath);

foreach (ZipArchiveEntry entry in archive.Entries)
{
if (Path.GetExtension(entry.FullName).Equals(".txt", StringComparison.OrdinalIgnoreCase))
{
string destinationPath = Path.Combine(@"C:\zip-sample\extract", entry.FullName);
Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
entry.ExtractToFile(destinationPath, overwrite: true);
}
}

6. ZipFileでよくあるエラーと対処法

6-1. IOExceptionが発生する原因と対策

IOExceptionは、ファイルが使用中、同名ファイルが存在する、出力先に書き込めないなどの理由で発生します。

よくある原因は次のとおりです。

原因対策
作成先ZIPがすでに存在する事前に削除する、別名で保存する
解凍先に同名ファイルがあるoverwriteFiles: trueを使う
ZIPファイルを別プロセスが使用中使用中のアプリを閉じる
出力先が読み取り専用権限や属性を確認する

対策例です。

C#
try
{
if (File.Exists(zipPath))
{
File.Delete(zipPath);
}

ZipFile.CreateFromDirectory(sourceDirectory, zipPath);
}
catch (IOException ex)
{
Console.WriteLine($"入出力エラーが発生しました: {ex.Message}");
}

6-2. UnauthorizedAccessExceptionが発生する原因と対策

UnauthorizedAccessExceptionは、アクセス権限がない場所へ読み書きしようとした場合に発生します。

たとえば、C:\Windows配下や他ユーザーのフォルダ、管理者権限が必要な場所へZIPを作成しようとすると発生することがあります。

C#
try
{
ZipFile.ExtractToDirectory(zipPath, extractPath, overwriteFiles: true);
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"アクセス権限がありません: {ex.Message}");
}

対策としては、アプリケーションが書き込み可能なフォルダを使います。

C#
string extractPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"MyApp",
"extract"
);

6-3. DirectoryNotFoundExceptionが発生する原因と対策

DirectoryNotFoundExceptionは、圧縮元フォルダや出力先の親フォルダが存在しない場合に発生します。

C#
if (!Directory.Exists(sourceDirectory))
{
Console.WriteLine("圧縮元フォルダが存在しません。");
return;
}

string? parentDirectory = Path.GetDirectoryName(zipPath);

if (!string.IsNullOrEmpty(parentDirectory))
{
Directory.CreateDirectory(parentDirectory);
}

ZipFile.CreateFromDirectory(sourceDirectory, zipPath);

Directory.CreateDirectoryは、すでにフォルダが存在していても問題なく呼び出せるため、事前準備として便利です。

6-4. InvalidDataExceptionが発生する原因と対策

InvalidDataExceptionは、ZIPファイルが壊れている、ZIP形式ではない、途中までしかダウンロードされていない、といった場合に発生します。

C#
try
{
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
catch (InvalidDataException ex)
{
Console.WriteLine($"ZIPファイルの形式が不正です: {ex.Message}");
}

対策としては、ファイルサイズが0ではないか、拡張子だけが.zipになっていないか、ダウンロードやアップロードが完了しているかを確認します。

C#
FileInfo fileInfo = new FileInfo(zipPath);

if (!fileInfo.Exists || fileInfo.Length == 0)
{
Console.WriteLine("ZIPファイルが存在しないか、空です。");
return;
}

6-5. 「ファイルが既に存在します」と表示される場合の対策

「ファイルが既に存在します」というエラーは、ZIP作成時や解凍時によく発生します。

ZIP作成時は、既存のZIPファイルを削除してから作成します。

C#
if (File.Exists(zipPath))
{
File.Delete(zipPath);
}

ZipFile.CreateFromDirectory(sourceDirectory, zipPath);

解凍時は、上書き可能なオーバーロードを使います。

C#
ZipFile.ExtractToDirectory(zipPath, extractPath, overwriteFiles: true);

上書きしたくない場合は、解凍先フォルダ名に日時を付ける方法が安全です。

C#
string extractPath = Path.Combine(
@"C:\zip-sample\extract",
DateTime.Now.ToString("yyyyMMdd_HHmmss")
);

ZipFile.ExtractToDirectory(zipPath, extractPath);

7. ZipFileを安全に使うための実装ポイント

7-1. パスの存在チェックを行う

ZIP処理では、圧縮元、ZIPファイル、解凍先のパスチェックが重要です。

C#
if (!Directory.Exists(sourceDirectory))
{
throw new DirectoryNotFoundException($"圧縮元が存在しません: {sourceDirectory}");
}

string? zipDirectory = Path.GetDirectoryName(zipPath);

if (!string.IsNullOrEmpty(zipDirectory))
{
Directory.CreateDirectory(zipDirectory);
}

解凍時は、ZIPファイルの存在も確認します。

C#
if (!File.Exists(zipPath))
{
throw new FileNotFoundException("ZIPファイルが見つかりません。", zipPath);
}

Directory.CreateDirectory(extractPath);

7-2. 例外処理を追加する

ファイル操作では、環境や権限、ディスク容量、ファイル使用状況によって例外が発生します。最低限、次のように例外処理を入れておくと、原因を追跡しやすくなります。

C#
try
{
ZipFile.ExtractToDirectory(zipPath, extractPath, overwriteFiles: true);
Console.WriteLine("解凍が完了しました。");
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"ファイルが見つかりません: {ex.Message}");
}
catch (DirectoryNotFoundException ex)
{
Console.WriteLine($"ディレクトリが見つかりません: {ex.Message}");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"アクセス権限がありません: {ex.Message}");
}
catch (InvalidDataException ex)
{
Console.WriteLine($"ZIPファイルが不正です: {ex.Message}");
}
catch (IOException ex)
{
Console.WriteLine($"入出力エラーです: {ex.Message}");
}

7-3. 解凍時のZip Slip対策

ZIP解凍では、Zip Slipと呼ばれるパストラバーサル攻撃に注意が必要です。悪意のあるZIPには、..\..\Windows\System32\...のように解凍先フォルダの外へファイルを書き込もうとするパスが含まれている場合があります。Microsoft公式ドキュメントでも、解凍時には解凍先ディレクトリを回避する悪意のあるファイルパスを確認する必要があると説明されています。Microsoft Learn+1

安全に特定フォルダへ解凍する例です。

C#
using System.IO.Compression;

static void SafeExtractToDirectory(string zipPath, string extractPath, bool overwrite)
{
string destinationDirectoryFullPath = Path.GetFullPath(extractPath);

if (!destinationDirectoryFullPath.EndsWith(Path.DirectorySeparatorChar))
{
destinationDirectoryFullPath += Path.DirectorySeparatorChar;
}

using ZipArchive archive = ZipFile.OpenRead(zipPath);

foreach (ZipArchiveEntry entry in archive.Entries)
{
string destinationPath = Path.GetFullPath(
Path.Combine(destinationDirectoryFullPath, entry.FullName)
);

if (!destinationPath.StartsWith(destinationDirectoryFullPath, StringComparison.OrdinalIgnoreCase))
{
throw new IOException($"不正なパスが含まれています: {entry.FullName}");
}

if (string.IsNullOrEmpty(entry.Name))
{
Directory.CreateDirectory(destinationPath);
continue;
}

Directory.CreateDirectory(Path.GetDirectoryName(destinationPath)!);
entry.ExtractToFile(destinationPath, overwrite);
}
}

外部からアップロードされたZIPを解凍する場合は、ZipFile.ExtractToDirectoryをそのまま呼ぶだけでなく、このようにエントリごとのパス検証を行う実装を検討してください。

7-4. 大容量ZIPファイルを扱う際の注意点

大容量ZIPファイルを扱う場合は、ディスク容量、処理時間、メモリ使用量、タイムアウトに注意します。特にWebアプリでアップロードされたZIPを処理する場合、同期的に長時間処理するとリクエストタイムアウトの原因になります。

対策としては、次のような実装を検討します。

課題対策
ディスク容量不足事前に空き容量を確認する
処理時間が長いバックグラウンドジョブ化する
ZIP爆弾展開後サイズやファイル数に上限を設ける
不要ファイル混入拡張子やファイル名を検証する
同時処理一時フォルダをリクエストごとに分ける

ZIP内のファイル数や合計サイズを確認する例です。

C#
using ZipArchive archive = ZipFile.OpenRead(zipPath);

long totalSize = 0;
int fileCount = 0;

foreach (ZipArchiveEntry entry in archive.Entries)
{
if (!string.IsNullOrEmpty(entry.Name))
{
fileCount++;
totalSize += entry.Length;
}
}

if (fileCount > 1000)
{
throw new InvalidOperationException("ファイル数が多すぎます。");
}

if (totalSize > 1024L * 1024L * 1024L)
{
throw new InvalidOperationException("展開後サイズが大きすぎます。");
}

7-5. パスワード付きZIPへの対応可否

ZipFileは、パスワード付きZIPの作成・解凍には対応していません。パスワード付きZIPが必要な場合は、SharpZipLib、DotNetZip、7-Zip連携などの外部ライブラリや外部ツールの利用を検討します。

ただし、外部ライブラリを使う場合は、保守状況、ライセンス、脆弱性対応、対応する暗号化方式を確認してください。業務システムでは「パスワード付きZIPをメール送信する」運用自体を見直し、セキュアなファイル共有サービスを利用する選択肢もあります。

8. ZipFileの実用サンプルコード

8-1. フォルダをZIP化してバックアップするコード

日付付きのZIPファイルを作成するバックアップ処理の例です。

C#
using System.IO.Compression;

static string BackupDirectory(string sourceDirectory, string backupDirectory)
{
if (!Directory.Exists(sourceDirectory))
{
throw new DirectoryNotFoundException($"圧縮元が存在しません: {sourceDirectory}");
}

Directory.CreateDirectory(backupDirectory);

string zipFileName = $"backup_{DateTime.Now:yyyyMMdd_HHmmss}.zip";
string zipPath = Path.Combine(backupDirectory, zipFileName);

ZipFile.CreateFromDirectory(
sourceDirectory,
zipPath,
CompressionLevel.Optimal,
includeBaseDirectory: true
);

return zipPath;
}

// 使用例
string createdZip = BackupDirectory(
@"C:\zip-sample\source",
@"C:\zip-sample\backups"
);

Console.WriteLine($"バックアップを作成しました: {createdZip}");

8-2. ZIPを指定フォルダに解凍するコード

ZIPを指定フォルダへ解凍するシンプルなコードです。

C#
using System.IO.Compression;

static void ExtractZip(string zipPath, string extractPath)
{
if (!File.Exists(zipPath))
{
throw new FileNotFoundException("ZIPファイルが見つかりません。", zipPath);
}

Directory.CreateDirectory(extractPath);

ZipFile.ExtractToDirectory(
zipPath,
extractPath,
overwriteFiles: true
);
}

// 使用例
ExtractZip(
@"C:\zip-sample\backup.zip",
@"C:\zip-sample\extract"
);

8-3. 圧縮と解凍をメソッド化するコード

圧縮と解凍を共通メソッド化しておくと、複数箇所から使い回せます。

C#
using System.IO.Compression;

public static class ZipHelper
{
public static void CompressDirectory(
string sourceDirectory,
string zipPath,
bool overwrite = false
)
{
if (!Directory.Exists(sourceDirectory))
{
throw new DirectoryNotFoundException($"圧縮元が存在しません: {sourceDirectory}");
}

if (File.Exists(zipPath))
{
if (!overwrite)
{
throw new IOException($"ZIPファイルは既に存在します: {zipPath}");
}

File.Delete(zipPath);
}

string? parentDirectory = Path.GetDirectoryName(zipPath);

if (!string.IsNullOrEmpty(parentDirectory))
{
Directory.CreateDirectory(parentDirectory);
}

ZipFile.CreateFromDirectory(
sourceDirectory,
zipPath,
CompressionLevel.Optimal,
includeBaseDirectory: false
);
}

public static void ExtractZip(
string zipPath,
string extractPath,
bool overwrite = true
)
{
if (!File.Exists(zipPath))
{
throw new FileNotFoundException("ZIPファイルが見つかりません。", zipPath);
}

Directory.CreateDirectory(extractPath);

ZipFile.ExtractToDirectory(
zipPath,
extractPath,
overwriteFiles: overwrite
);
}
}

使用例です。

C#
ZipHelper.CompressDirectory(
@"C:\zip-sample\source",
@"C:\zip-sample\backup.zip",
overwrite: true
);

ZipHelper.ExtractZip(
@"C:\zip-sample\backup.zip",
@"C:\zip-sample\extract",
overwrite: true
);

8-4. エラー処理込みの実践コード

最後に、実運用を想定したエラー処理込みのサンプルです。

C#
using System.IO.Compression;

static bool TryCreateZip(string sourceDirectory, string zipPath)
{
try
{
if (!Directory.Exists(sourceDirectory))
{
Console.WriteLine("圧縮元フォルダが存在しません。");
return false;
}

string? zipDirectory = Path.GetDirectoryName(zipPath);

if (!string.IsNullOrEmpty(zipDirectory))
{
Directory.CreateDirectory(zipDirectory);
}

if (File.Exists(zipPath))
{
File.Delete(zipPath);
}

ZipFile.CreateFromDirectory(
sourceDirectory,
zipPath,
CompressionLevel.Optimal,
includeBaseDirectory: true
);

Console.WriteLine("ZIP作成が完了しました。");
return true;
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"アクセス権限エラー: {ex.Message}");
}
catch (IOException ex)
{
Console.WriteLine($"入出力エラー: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期しないエラー: {ex.Message}");
}

return false;
}

解凍側も同様に、例外処理を入れておくと安全です。

C#
static bool TryExtractZip(string zipPath, string extractPath)
{
try
{
if (!File.Exists(zipPath))
{
Console.WriteLine("ZIPファイルが存在しません。");
return false;
}

Directory.CreateDirectory(extractPath);

ZipFile.ExtractToDirectory(
zipPath,
extractPath,
overwriteFiles: true
);

Console.WriteLine("ZIP解凍が完了しました。");
return true;
}
catch (InvalidDataException ex)
{
Console.WriteLine($"ZIPファイルが壊れている可能性があります: {ex.Message}");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine($"アクセス権限エラー: {ex.Message}");
}
catch (IOException ex)
{
Console.WriteLine($"入出力エラー: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期しないエラー: {ex.Message}");
}

return false;
}

9. ZipFileを使うときのよくある質問

9-1. C#のZipFileでパスワード付きZIPは作成できる?

標準のZipFileでは、パスワード付きZIPの作成はできません。パスワード付きZIPが必要な場合は、外部ライブラリや7-Zipなどの外部ツール連携を検討してください。

ただし、セキュリティ要件によっては、パスワード付きZIPよりも、アクセス制御付きのクラウドストレージやファイル共有サービスを使う方が適している場合があります。

9-2. ZipFileで1つのファイルだけ圧縮できる?

ZipFile.CreateFromDirectoryはフォルダをZIP化するメソッドなので、1ファイルだけを直接指定してZIP化する用途には向いていません。

1つのファイルだけをZIPに入れたい場合は、ZipArchiveCreateEntryFromFileを使います。

C#
using System.IO.Compression;

string zipPath = @"C:\zip-sample\single.zip";
string filePath = @"C:\zip-sample\sample.txt";

using ZipArchive archive = ZipFile.Open(zipPath, ZipArchiveMode.Create);

archive.CreateEntryFromFile(
filePath,
Path.GetFileName(filePath),
CompressionLevel.Optimal
);

9-3. ZipFileで既存ZIPに追記できる?

既存ZIPにファイルを追記する場合は、ZipFile.OpenZipArchiveMode.Updateを指定します。

C#
using ZipArchive archive = ZipFile.Open(
@"C:\zip-sample\backup.zip",
ZipArchiveMode.Update
);

archive.CreateEntryFromFile(
@"C:\zip-sample\new.txt",
"new.txt"
);

ただし、同じエントリ名がすでに存在する場合の扱いには注意が必要です。必要に応じて既存エントリを削除してから追加します。

C#
ZipArchiveEntry? oldEntry = archive.GetEntry("new.txt");

oldEntry?.Delete();

archive.CreateEntryFromFile(
@"C:\zip-sample\new.txt",
"new.txt"
);

9-4. ZipFileと外部ライブラリはどちらを使うべき?

通常のZIP圧縮・解凍であれば、まずは標準のZipFileを使うのがおすすめです。追加ライブラリが不要で、メンテナンスもしやすいためです。

一方、次のような要件がある場合は、外部ライブラリを検討します。

要件選択肢
パスワード付きZIP外部ライブラリ、7-Zip連携
AES暗号化ZIP外部ライブラリ
特殊な文字コード対応外部ライブラリまたはエンコーディング指定
細かい圧縮形式の制御外部ライブラリ
ZIP以外の7z、rar対応専用ツールや外部ライブラリ

標準機能で足りるならZipFile、標準機能では要件を満たせないなら外部ライブラリ、という判断で問題ありません。

9-5. 文字化けする場合はどうすればよい?

ZIP内のファイル名が文字化けする場合は、ExtractToDirectoryZipArchiveのエンコーディング指定を確認します。UTF-8で作成されたZIPならEncoding.UTF8、古いWindows環境や一部ツールで作成されたZIPならShift_JISを試します。

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

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

ZipFile.ExtractToDirectory(
@"C:\zip-sample\japanese.zip",
@"C:\zip-sample\extract",
Encoding.GetEncoding("shift_jis")
);

ただし、すべての文字化けがエンコーディング指定だけで解決するとは限りません。ZIP作成元のツール、OS、ファイル名の文字種、.NETのバージョンも含めて確認しましょう。

まとめ

C#でZIP圧縮・解凍を行うなら、標準ライブラリのZipFileを使うのが基本です。フォルダ全体をZIP化する場合はZipFile.CreateFromDirectory、ZIP全体を解凍する場合はZipFile.ExtractToDirectoryを使えば、少ないコードで実装できます。

一方で、ZIP内の個別ファイルを操作したい場合はZipArchiveZipArchiveEntryを組み合わせます。ファイル一覧の取得、既存ZIPへのファイル追加、特定ファイルだけの解凍などは、ZipFile単体ではなくZipArchiveを使うと柔軟に対応できます。

実運用では、既存ファイルの上書き、アクセス権限、文字化け、壊れたZIP、Zip Slip対策、大容量ZIPへの制限などを必ず考慮しましょう。特に外部から受け取ったZIPを解凍する場合は、解凍先フォルダの外へファイルが展開されないようにパス検証を行うことが重要です。

csharp zipfileで実装方法を探している場合は、まず標準のSystem.IO.Compression.ZipFileで要件を満たせるか確認し、パスワード付きZIPや特殊な暗号化が必要な場合のみ外部ライブラリを検討するとよいでしょう。