7-Zip ライブラリとしての CubeICE

圧縮・解凍ソフト CubeICE をゼロから改修 に記載した通り、昨年、CubeICE に対して大幅な修正を実施しましたが、この際にライブラリとしても利用できるようにインターフェース(クラスやメソッド)を整理する事をテーマの一つに設定していました。そこで、この記事では CubeICE (正確には Cube.FileSystem.SevenZip.dll)を 7-Zip ライブラリとして利用するための方法について記載します。

事前準備

Cube.FileSystem.SevenZip は NuGet 経由で取得する事ができますが、この中には 7z.dll が含まれないので別途ダウンロードして実行ディレクトリに配置して下さい。7z.dll は www.7-zip.org から取得できる他、オリジナルに対して Unicode ビルドする等いくつかの修正を加えたものを GitHub releases に公開しています(また、修正版のリポジトリcube-soft/7z で公開しています)。尚、C# 等から Unmanaged な dll を利用する場合 x86/x64 の設定が問題になるので、特に Any CPU でビルドする場合、どちらの dll を必要とするか注意して下さい。

圧縮ファイルの解凍・展開

圧縮ファイルを解凍・展開するサンプルコードは下記になります。尚、全てのコードで "using Cube.FileSystem.SeventZip;" が記述されているものとします。

// Set password directly or using Query<string>
var password = new Cube.Query<string>(e =>
{
    e.Result = "password";
    e.Cancel = false;
});

using (var reader = new ArchiveReader(@"path\to\archive", password))
{
    var progress = new Progress<Report>(e => DoSomething(e));
    reader.Filters = new[] { ".DS_Store", "Thumbs.db", "__MACOSX", "desktop.ini" };
    reader.Extract(@"path\to\directory", progress);
}

ArchiveReader クラスは、オブジェクト生成時の第 1 引数に指定されたファイルに対して、解凍・展開するために必要な 7-Zip モジュールを自動的にロードします。また、第 2 引数はパスワードを表し、文字列を直接指定できる他、Cube.IQuery<string> を実装したオブジェクトを指定する事も可能です。このオブジェクトは、「パスワード・ダイアログ」などを通じてユーザにパスワードを尋ねるようなインターフェースを実現する際に利用します。

Filters プロパティは CubeICE のフィルタリング機能をライブラリとして実装したもので、解凍・展開時に除外するファイルまたはフォルダの一覧を指定します。最後に Extract メソッドに解凍・展開後のフォルダのパスを指定して、処理を開始します。この時、第 2 引数に System.IProgress<Report> を実装したオブジェクトを指定すると、このオブジェクトを通じて処理状況が通知されます。

using (var reader = new ArchiveReader(@"path\to\archive", "password"))
{
    // Save as "path\to\directory\{item.FullName}"
    reader.Items[0].Extract(@"path\to\directory");
}

ArchiveReader クラスの Items プロパティでは圧縮ファイルに含まれる各項目の情報 (ArchiveItem) を取得できますが、ArchiveItem に定義されている Extract メソッドを実行する事で個別に解凍・展開する事も可能です。ただし、開発環境で検証した限りですが、解凍・展開するフォーマットによっては処理時間が増大する場合があるようです。そのため、このメソッドは、あくまでも特定ファイルを解凍・展開したい時に使用する程度に留めておいた方が良いかもしれません。

圧縮ファイルの生成

圧縮ファイルを生成するサンプルコードは下記になります。

using (var writer = new ArchiveWriter(Format.Zip))
{
    writer.Add(@"path\to\file");
    writer.Add(@"path\to\directory_including_files");
    writer.Option  = new ZipOption { CompressionLevel = CompressionLevel.Ultra };
    writer.Filters = new[] { ".DS_Store", "Thumbs.db", "__MACOSX", "desktop.ini" };
    
    var progress = new Progress<Report>(e => DoSomething(e));
    writer.Save(@"path\to\save.zip", "password", progress);
}

ArchiveWriter クラスは、まず、圧縮形式を指定してオブジェクトを生成します。Format は、圧縮と解凍・展開で共用しているため大量の定義が存在しますが、ArchiveWriter として対応しているものは Zip, SevenZip, Tar, GZip, BZip2, XZ の 6 種類となります。そして、Add メソッドを通じて、生成されたオブジェクトに対してファイルまたはフォルダを追加していきます。尚、フォルダが指定された場合、フォルダに含まれるファイルおよびフォルダが再帰的に追加されていきます。最後に、Save メソッドに保存パスを指定して圧縮処理を開始します。

Option プロパティには圧縮時の各種オプションを指定する事ができます。指定可能な内容については、ArchiveOption.cs を参照下さい。まだ、基本的なオプションにしか対応できていませんが、追々、その他のものにも対応していく予定です。また、Filters プロパティおよび、Save メソッドの System.IProgress<Report> 引数の役割は解凍・展開の時と同様です。

using (var writer = new ArchiveWriter(Format.Tar))
{
    writer.Option = new TarOption
    {
        CompressionMethod = CompressionMethod.BZip2, // GZip, BZip2, XZ or Copy
        CompressionLevel  = CompressionLevel.Ultra,
    };

    writer.Add(@"path\to\file");
    writer.Add(@"path\to\directory_including_files");
    writer.Save(@"path\to\save.tar.gz");
}

*.tar.gz のように Tar 系の圧縮ファイルを生成する場合、ArchiveWriter のコンストラクタには Format.Tar を指定してオブジェクトを生成します。その上で、Option プロパティに対して、TarOption クラスを利用して圧縮方式を指定します。

進捗状況のレポート機能

System.IProgress<Report> を通じて通知される進捗内容は下記になります。

public class Report
{
    public ReportStatus Status { get; }
    public public Information Current { get; }
    public long Count { get; }
    public long TotalCount { get; }
    public long Bytes { get; }
    public long TotalBytes { get; }
    public double Ratio => TotalBytes > 0 ? Bytes / (double)TotalBytes : 0.0;
}

TotalCount は圧縮または解凍・展開対象となるファイルおよびフォルダの総数、Count は Bytes は処理の終了した数を示し、TobalBytes および Bytes はバイト数に関する内容を表します。Current は現在処理中のファイルまたはフォルダの情報を保持し、Status は Current の処理状況 を Begin(開始直前)、End(終了)、Progress(処理中)で示します。

尚、Status および Current に関しては現時点では解凍・展開用と言う性質が強く、圧縮時にはあまり信頼できません(一応 Begin は通知されますが、End の通知は未実装)。これは、圧縮処理は 7-Zip 自体がマルチスレッド対応している事もあり、正確に通知する事が難しいと言う課題が残っているためです。この辺りをどうするかは、今後、検討していきます。