最初に
利用している主なクラスはこちら。
- ZipFile クラス
- ZipArchive クラス
これらのクラスは .NET Framework 4.5 から追加されています。
プログラムで zip ファイルを作成する例として、JAVA の jar ファイルや Android の apk ファイルのようなものがわかりやすく、関連するファイルをひとまとめにして保存することがあります。
Windows の古いノベルゲームなんかは、音声ファイルや画像ファイルがひとつひとつファイルとして用意されていたものがあって、データの展開や削除にとても時間がかかったりしました。その後は、ファイルをまとめてひとつに圧縮・暗号化される流れとなり、諸々効率的になりました。
そんなわけで、データファイルを作成するとき、1つのテキストデータだけだと表現しづらい複雑なデータの保存は、zip ファイルに圧縮してしまおうというサンプルの記事です。よくあるケースだと、「すでにあるファイルをまとめて zip に圧縮する」だと思いますが、ここでのサンプルは「メモリー上のテキストデータを zip にいくつかのファイルにして保存できる」です。
ZIP ファイルを扱うための基本的なコード
以下のコードを実行するときは、次のアセンブリを追加する必要があります。
- System.IO.Compression
- System.IO.Compression.FileSystem
ZIP ファイルの作成
まず、実際のファイルのひな形を用意するソースコードです。
Create("abc.zip"); private void Create(string zipPath) { if (File.Exists(zipPath)) { throw new System.IO.IOException("すでに同じ名前のファイル、または、フォルダーが存在しています。"); } var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create); zip.Dispose(); }
zip ファイル(0 KB)を作成できますが、中身がないと壊れたファイルとして認識されるかもしれません。通常は、Write メソッドを使って zip ファイルを生成しつつ、中身のデータを追加すると思います。
ZIP ファイルのエントリー(ファイル、ディレクトリー)を調べる
abc.zip
ファイルの中に sample.txt
というテキストファイルが存在しているかどうかを調べます。圧縮ファイルの中にディレクトリー構造があって、たとえば data
ディレクトリーの中に同じ名前のテキストファイルがあったとすると、data/sample.txt
がエントリー名になります。
Exists("abc.zip", "sample.txt"); private bool Exists(string zipPath, string name) { using (var zip = ZipFile.OpenRead(zipPath)) { var selectedFile = zip.Entries.FirstOrDefault(p => p.FullName == name); return selectedFile != null; } } private IEnumerable<string> EnumerateFiles(string zipPath) { var files = new List<string>(); using (var zip = ZipFile.OpenRead(zipPath)) { foreach (var entry in zip.Entries) { files.Add(entry.FullName); Console.WriteLine(entry.FullName); } } return files; }
ZIP ファイルにあるテキストデータを読み込む
最初から末尾まで一度にテキストデータとして読み込むパターンと、一行ずつテキストデータをコレクションにして読み込むパターンです。
ReadToEnd("abc.zip", "sample.txt"); private string ReadToEnd(string zipPath, string name) { using (var zip = ZipFile.OpenRead(zipPath)) { var selectedFile = zip.Entries.FirstOrDefault(p => p.FullName == name); if (selectedFile == null) throw new System.IO.FileNotFoundException(); using (var reader = new StreamReader(selectedFile.Open())) { return reader.ReadToEnd(); } } } private IEnumerable<string> ReadLine(string zipPath, string name) { var lines = new List<string>(); using (var zip = ZipFile.OpenRead(zipPath)) { var selectedFile = zip.Entries.FirstOrDefault(p => p.FullName == name); if (selectedFile == null) throw new System.IO.FileNotFoundException(); using (var reader = new StreamReader(selectedFile.Open())) { string line; while((line = reader.ReadLine()) != null) { lines.Add(line); } } } return lines; }
ZIP ファイルにテキストデータのファイルを追加する
テキストデータのファイルを追加するときは、zip ファイル内にすでに同名のファイル(エントリー)があるかどうかを確認する必要があります。
すでに、同じ名前のファイルが存在しているときは、いわゆる、追記や上書きに相当する方法が無いため、ファイルを消して新しく追加するか、上書きされるファイルのデータを読み込んで、読み込んだデータ+新しいデータとして再保存することになります。
private void Write(string zipPath, string name, string text, bool overwrite = false) { var beforeText = ""; if (Exists(zipPath, name)) { if (overwrite == false) throw new System.IO.IOException("すでに同じ名前のファイル、または、フォルダーが存在しています。"); beforeText = ReadToEnd(zipPath, name); Delete(zipPath, name); } using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Update)) { var newFile = zip.CreateEntry(name); using (var writer = new StreamWriter(newFile.Open(), System.Text.Encoding.UTF8)) { if (!string.IsNullOrEmpty(beforeText)) { writer.Write(beforeText); } writer.Write(text); } } } private void WriteLine(string zipPath, string name, string text, bool overwrite = false) { var beforeText = ""; if (Exists(zipPath, name)) { if (overwrite == false) throw new System.IO.IOException(); beforeText = ReadToEnd(zipPath, name); Delete(zipPath, name); } using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Update)) { var newFile = zip.CreateEntry(name); using (var writer = new StreamWriter(newFile.Open(), System.Text.Encoding.UTF8)) { if (!string.IsNullOrEmpty(beforeText)) { writer.Write(beforeText); } writer.WriteLine(text); } } }
追加するファイルの圧縮率は以下のようにエントリーを作成するときに設定することができます。
var file = zip.CreateEntry(entryName, CompressionLevel.Fastest);
識別子 | 圧縮方法 | アクセス速度 |
---|---|---|
Optimal | 高圧縮 | 低速 |
Fastest | 低圧縮率 | 中速 |
NoCompression | 無圧縮 | 高速 |
Delete メソッドはすぐ「↓」のとおりです。
ZIP ファイル内のファイル(エントリー)を削除する
削除は、シンプルなんで特にコメントなし。
Delete("abc.zip", "sample.txt"); public void Delete(string zipPath, string name) { using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Update)) { var selectedFile = zip.Entries.FirstOrDefault(p => p.FullName == name); if (selectedFile == null) throw new System.IO.FileNotFoundException(); selectedFile.Delete(); } }
ZIP ファイル内にディレクトリーを作成する
基本的には Write
メソッドと同じだけど、エントリーの末尾に /
か \
をつけておくと、ディレクトリーとして追加されます。Write メソッドで dir/sample.txt
でエントリーを追加すると、ディレクトリーを作成しつつファイルを追加することになるので、通常はあまり利用しません。
public void MakeDirectory(string zipPath, string directoryName) { if (Exists(directoryName, true)) { throw new System.IO.IOException("すでに同じ名前のファイル、または、フォルダーが存在しています。"); } using (var zip = ZipFile.Open(zipPath, ZipArchiveMode.Update)) { if (!directoryName.EndsWith(@"/") && !directoryName.EndsWith(@"\")) { directoryName += "/"; } zip.CreateEntry(directoryName); } }
サンプル
テストプログラムは GitHub の「Samples」に公開しています。今回のプログラムは「ZipFileTest」です。
ZipFile
クラスを操作するためのサンプルのアダプタークラスSimpleZip
を作成してみました。
参考
- 作者:昌達 慶仁
- 発売日: 2010/10/26
- メディア: 単行本