【C#】.NET標準機能でZipファイルを操る(ZipFile、ZipArchive)

C#入門
この記事は約9分で読めます。

先日、Zip圧縮された大量のファイルから、特定のフォルダに格納されているファイルのみ解凍する必要に迫られました。

そういえば、今までも大量のZipファイルを解凍したことが何度かあったような・・・

そこで、これを機に .NET 標準機能だけで Zipファイルの解凍、圧縮を行うクラスを作ったので、紹介したいと思います。

もちろん、指定した条件に合致するファイルのみ解凍することも可能です。

興味のある方は、是非ご一読ください。

ZipFile、ZipArchiveクラスについて

.NET Framework 4.5 以上と、.NetCore から、標準で Zipファイルを扱うためのクラスである、ZipFile、ZipArchive が用意されました。

下図は、この記事で登場するクラスの関係を表した図です。

ZipFile クラスは、ZipFileをオープンするためのクラスです。

単にフォルダを丸ごと圧縮するとか、Zipファイルを単純に解凍するだけなら、このクラスだけで事が足ります。

ZipArchiveクラスは、指定したファイルを書庫に追加する時に使います。

ZipArchiveEntryクラスは、書庫内のエントリ情報(ファイル名、書庫内のフォルダ階層、圧縮サイズ、最終更新日時など)を保持していますが、この情報を使って任意のファイルの解凍や、書庫内のエントリからの削除が行えます。

エントリについて

エントリとは、書庫内に格納されたファイルパスのことです。

ルートからファイルに到達するまでのフォルダ名、ファイル名は、スラッシュ( ‘/’ )で区切るところが、通常のファイルパスとの違いです。

例えば下図を例にすると、Window1.xaml.cs を指定したい場合、ルートが Deliverables となり、Source の下にWindow1.xaml.cs があるので、次のように記述します。

 Deliverables/Source/Window1.xaml.cs

同様に、Suites.Utils.dll を指定する場合は、次のようになります。

 Deliverables/Source/bin/Suites.Utils.dll

ZipFile、ZipArchiveを使うための準備

ZipFile、ZipArchive クラスを使うには、あらかじめ プログラム先頭に下記の1行を追加しておいてください。

次に、.Net Core では必要ありませんが、.Net Framework で使う場合には、次のアセンブリを参照設定しておく必要があります。

  • System.IO.Compression
  • System.IO.Compression.FileSystem

以上で準備は完了です。

ZipFile、ZipArchiveクラスの使い方

それでは、一通りに使い方について簡単なソースコードを紹介していきます。

フォルダを丸ごと圧縮する

ZipFile.CreateFromDirectory(ソースディレクトリ,書庫のパス ,[圧縮レベル],[ベースディレクトリの追加]);

圧縮レベルは Fastest、Optimal、SmallestSize、NoCompression の4種類が選べます。

省略した場合は、Optimal が選択されます。

速度優先で圧縮CompressionLevel.Fastest
速度とサイズのバランスを取って圧縮CompressionLevel.Optimal
サイズ優先で圧縮CompressionLevel.SmallestSize
無圧縮CompressionLevel.NoCompression 

ベースディレクトリの追加をtrue にした場合と false にした場合の違いは以下の通りです。

フォルダ丸ごと解凍

ZipFile.ExtractToDirectory(書庫のパス, 解凍先フォルダ)

尚、展開先のフォルダ内にファイルが存在する場合、エラーとなります。

.NET Core では、3番目の引数に overwriteFiles というオプションが追加されており、これを true にすることで、既に展開済みフォルダがあっても上書きすることが可能です。

書庫内のエントリ(フォルダ、フォルダ名)を取り出す

ZipFile.OpenRead(書庫のパス) で書庫をオープン後、 Entries でエントリを取得

ZipArchiveEntry には、以下のプロパティが指定できます。

Nameファイル名
FullNameエントリ
Length元のサイズ
CompressedLength圧縮サイズ
LastWriteTime最終更新日時
ExternalAttributesOSおよびアプリケーション固有のファイル属性
Crc3232ビット巡回冗長検査(.NET Core利用時のみ参照可能)

例えば、tempフォルダの中に存在するフォルダのみ解凍したい場合を考えてみます。

通常は、ループの中でFullName を参照し、temp が含まれていれば、そのエントリを解凍するような処理を考えますが、temp の中に 空のフォルダ(今回の例では dummy)のエントリがあると、それも取得されてしまいます。

後述する ExtractToFile に空フォルダのエントリ渡すとエラーになってしまうので、空フォルダのエントリか否かを識別したくなります。

幸いなことに、空フォルダのエントリだと Name プロパティ は空文字になるため、Name != “” の条件を付け加えることで識別可能です。

指定したファイルのみ解凍

ZipFile.OpenRead(書庫のパス) で書庫をオープン後、GetEntry(エントリ) で対象ファイルを特定、ZipArchiveEntryのExtractToFile(出力ファイル名,[上書きモード]) で取り出します。

書庫から指定したファイルを削除

ZipFile.Open(書庫のパス, ZipArchiveMode.Update) で書庫をオープン後、GetEntry(エントリ) で対象ファイルを特定、ZipArchiveEntry の Delete() で削除します。

書庫にファイルを追加

ZipFile.Open(書庫のパス, ZipArchiveMode.Update) で書庫をオープン後、CreateEntryFromFile(追加したいファイル名,追加先のエントリ,[圧縮レベル]) でファイルを追加します。

解凍せず指定したファイルの中身を取得

ZipFile.OpenRead(書庫のパス) で書庫をオープン後、GetEntry(エントリ) で対象ファイルを特定、ZipArchiveEntryのOpen()で生成した Stream を使って中身を取得します。

自作クラスのソースコード

いままでの内容を元に、少しだけ使いやすくしたクラスを作ってみました。

namaspace は CommonClass 、クラス名は Zip としています。

リファレンス

本クラスが実装しているメソッドは次の通りです。

指定フォルダの圧縮void Compress(
 string folder,
 string zipFile,
bool isOverwrite = false,
CompressionLevel compressionLevel = CompressionLevel.Optimal
)
書庫の解凍void Extract(
string zipFile,
string outputFolder = “”
)
書庫の解凍(条件指定)int Extract(
string zipFile,
string outputFolder,
Func func,
bool isOverwrite = false
)
書庫内のファイル名取得string[] Files(
string zipFile,
string format = “{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}”
)
エントリ一覧取得IEnumerable Entries(string zipFile)
指定ファイルの解凍bool TakeOut(
string zipFile,
string entryName,
string outuptName = “”,
bool isOverwrite = false
)
指定エントリの削除bool Delete(
string zipFile,
string entryName
)
書庫へのファイル追加void Append(
string zipFile,
string fileName,
string entryPath = “”,
CompressionLevel compressionLevel = CompressionLevel.Optimal
)
解凍せず中身を参照string Read(
string zipFile,
string entryPath,
string encoderName = “UTF-8”
)

使い方

Zipのインスタンスを生成し、必要なメソッドを読んでください。

具体的なサンプルは次の通りです。

ソースコード

今回のソースコード一式です。

.NET Core への対応

.NET Core で利用する場合は、必要に応じてExtract(第3引数に Func<ZipArchiveEntry, bool> func引数を指定しない方)と Files メソッドを下記に置き換えてください。

Extractを下記の内容に置き換えると、解凍時にフォルダがある場合、上書きできるようになります。

Filesを下記のものに置き換えると、Crc32 (32ビット巡回冗長検査)が参照できるようになります。

まとめ

今回は必要に駆られて C#でZipファイルを扱うためのクラスを作成したので、ZipFile、ZipArchive の全体像、基本的な使い方、そしてこれらを使った自作の便利クラスの紹介を行いました。

フリーの圧縮解凍ツールがあるので、プログラムで圧縮解凍を行うケースは少ないかもしれませんが、圧縮するファイルがあちこにのフォルダにまたがっていたり、大量のZIPファイルから特定の条件を満たすファイルのみ解凍したいような場合は、手作業だと非常に面倒くさくなります。

そんな時、この記事を参考にして頂ければ幸いです。

タイトルとURLをコピーしました