Zip for WPF/Silverlight
ファイルの圧縮
チュートリアル > ファイルの圧縮

このチュートリアルでは、個々のファイルを圧縮および展開する方法について説明します。これらは zip ファイルではなく、ディスク上の圧縮ストリームが対象であることに注意してください。最終アプリケーションは、次の図のように表示されます。

手順 1:メインフォームを作成します。

Visual Studio のツールボックスから、新しい WPF/Silverlight プロジェクトを開始し、フォームに次のコントロールを追加します。

手順 2: C1.WPF.Zip/C1.Silverlight.Zip アセンブリに参照を追加します。

ソリューションエクスプローラウィンドウに移動し、[すべてのファイルを表示]ボタンをクリックします。[参照]を右クリックし、[参照の追加]メニューオプションを選択します。リストから C1.WPF.Zip/C1.Silverlight.Zip アセンブリを選択するか、ファイルを参照して C1.WPF.Zip.dll/C1.Silverlight.Zip.dll ファイルを探します。

[MainPage.xaml.vb]タブ(C# では[MainPage.xaml.cs]タブ)を選択するか、[表示]→[コード]を選択して、コードエディタを開きます。ファイルの上部に、次のステートメントを追加します。

コードのコピー
Imports System.IO
Imports C1.C1Zip
コードのコピー
using System.IO;
using C1.C1Zip;

これで、C1.WPF.Zip,/C1.Silverlight.Zip アセンブリと System.IO アセンブリで定義されているオブジェクトがプロジェクトから可視になり、タイピング量を大きく減らすことができます。

手順 3:圧縮ファイルと展開後ファイルのディレクトリ名を定義します。

フォームのコードエディタで、次の定数を定義します。

コードのコピー
Private Const DIR_COMP = "\compressed"
Private Const DIR_EXP = "\expanded"
コードのコピー
private const string DIR_COMP = @"\compressed";
private const string DIR_EXP = @"\expanded";

これらは、圧縮ファイルと展開後ファイルを保存するディレクトリの名前です(ディスク上のチュートリアルアプリケーションが配置されているディレクトリからの相対ディレクトリ)。

手順 4:ファイルを圧縮するコードを追加します。

ファイルの圧縮]コマンドボタンの Click イベントを処理する次のコードを追加します。

コードのコピー
Private Sub btnCompress_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCompress.Click
    ' アプリケーションディレクトリを取得します。
    Dim appPath As String = Application.ExecutablePath
    Dim i As Integer = appPath.IndexOf("\bin\")
    If i > 0 Then appPath = appPath.Substring(0, i)
    ' 圧縮ファイルのディレクトリを作成します。
    If (Directory.Exists(appPath + DIR_COMP)) Then
        Directory.Delete(appPath + DIR_COMP, True)
    End If
    Directory.CreateDirectory(appPath + DIR_COMP)
    ' 圧縮統計値を収集する準備をします。
    Dim count As Long
    Dim size As Long
    Dim sizeCompressed As Long
    Dim ticks As Long = DateTime.Now.Ticks
    ' アプリケーションディレクトリにあるすべてのファイルを圧縮ディレクトリに圧縮します。
    Dim files As String() = Directory.GetFiles(appPath)
    Dim srcFile As String
    For Each srcFile In files
        Dim dstFile As String
        dstFile = appPath + DIR_COMP + "\" + Path.GetFileName(srcFile) + ".cmp"
        ' ファイルを圧縮します。
        CompressFile(dstFile, srcFile)
        ' 統計値を更新します。
        count = count + 1
        size = size + New FileInfo(srcFile).Length
        sizeCompressed = sizeCompressed + New FileInfo(dstFile).Length
    Next srcFile
    ' 統計値を表示します。
    Dim msg As String = String.Format("Compressed {0} files in {1} ms." & vbCrLf & "Original size:   {2:#,###}" & vbCrLf & "Compressed size: {3:#,###} ({4:0.00}% of original)", count, (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond, size, sizeCompressed, (sizeCompressed / size) * 100.0)
    Label1.Text = msg
    ' これで、展開することができます。
    btnExpand.Enabled = True
End Sub
コードのコピー
private void btnCompress_Click(object sender, EventArgs e)
{
    // アプリケーションディレクトリを取得します。
    string appPath = Application.ExecutablePath;
    int i = appPath.IndexOf(@"\bin\");
    if (i > 0) appPath = appPath.Substring(0, i);
    // 圧縮ファイルのディレクトリを作成します。
    if ((Directory.Exists(appPath + DIR_COMP)))
        Directory.Delete(appPath + DIR_COMP, true);
    Directory.CreateDirectory(appPath + DIR_COMP);
    // 圧縮統計値を収集する準備をします。
    long count = 0;
    long size = 0;
    long sizeCompressed = 0;
    long ticks = DateTime.Now.Ticks;
    // アプリケーションディレクトリにあるすべてのファイルを圧縮ディレクトリに圧縮します。
    foreach (string srcFile in Directory.GetFiles(appPath))
    {
        string dstFile = appPath + DIR_COMP + Path.GetFileName(srcFile) + ".cmp";
        // ファイルを圧縮します。
        CompressFile(dstFile, srcFile);
        // 統計値を更新します。
        count++;
        size += new FileInfo(srcFile).Length;
        sizeCompressed += new FileInfo(dstFile).Length;
    }
    // 統計値を表示します。
    string  msg = string .Format("Compressed {0} files in {1} ms.\n\r" + "Original size:   {2:#,###}\n\r" + "Compressed size: {3:#,###} ({4:0.00}% of original)", count, (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond, size, sizeCompressed, (sizeCompressed / size) * 100.0);
    label1.Text = msg;
    // これで、展開することができます。
    btnExpand.Enabled = true;
}

重要な行は、ユーティリティ関数 CompressFile の呼び出しです。ここで、選択されたファイルをそれぞれ圧縮します。圧縮ファイルは、アプリケーションフォルダにある ¥compressed ディレクトリに保存されます。ファイル名は、元のファイルの名前に拡張子 CMP が付加された名前になります。

次のように CompressFile 関数のコードを追加します。

コードのコピー
Private Function CompressFile( dstFile As String, srcFile As String) As Boolean
    ' ファイルを圧縮する準備をします。
    Dim retval As Boolean = True
    Dim srcStream As FileStream = Nothing
    Dim dstStream As FileStream = Nothing
    Try
        ' ファイルを開きます。
        srcStream = New FileStream(srcFile, FileMode.Open, FileAccess.Read)
        dstStream = New FileStream(dstFile, FileMode.Create, FileAccess.Write)
        ' 書き込み先のファイルの圧縮プログラムストリームを開きます。
        Dim sw As C1ZStreamWriter = New C1ZStreamWriter(dstStream)
        ' ソースを圧縮プログラムストリームにコピーします。
        StreamCopy(sw, srcStream)
    Catch
        ' 例外? 呼び出し元に処理が失敗したことを通知します。
       retval = False
    Finally
        ' 必ずストリームを閉じます。
        If Not (srcStream Is Nothing) Then srcStream.Close()
        If Not (dstStream Is Nothing) Then dstStream.Close()
    End Try
    ' 完了。
    CompressFile = False
End Function
コードのコピー
private bool CompressFile(string  dstFile, string  srcFile)
{
    // ファイルを圧縮する準備をします。
    bool retval = true;
    FileStream srcStream = null;
    FileStream dstStream = null;
    try
    {
        // ファイルを開きます。
        srcStream = new FileStream(srcFile, FileMode.Open, FileAccess.Read);
        dstStream = new FileStream(dstFile, FileMode.Create, FileAccess.Write);
        // 書き込み先のファイルの圧縮プログラムストリームを開きます。
        C1ZStreamWriter sw = new C1ZStreamWriter(dstStream);
        // ソースを圧縮プログラムストリームにコピーします。
        StreamCopy(sw, srcStream);
    }
    catch
    {
        // 例外? 呼び出し元に処理が失敗したことを通知します。
        retval = false;
    }
    finally
    {
        // 必ずストリームを閉じます。
        if (srcStream != null) srcStream.Close();
        if (dstStream != null) dstStream.Close();
    }
    // 完了。
    return false;
}

この関数は、最初に新しいファイルストリームを2つ作成します。1つはソースファイル用、もう1つは圧縮ファイル用です。次に、C1ZStreamWriter オブジェクトを作成し、それを出力先ストリームにアタッチします。次に、StreamCopy 関数を呼び出して、ソースファイルからデータを転送し、圧縮プログラムストリームに書き込みます。

StreamCopy 関数は、ストリーム間でバイトを単純にコピーします。次にコードを示します。

コードのコピー
Private Sub StreamCopy(dstStream As Stream, srcStream As Stream)
    Dim buffer(32768) As Byte
    Dim read As Integer
    Do
        read = srcStream.Read(buffer, 0, buffer.Length)
        dstStream.Write(buffer, 0, read)
    Loop While read > 0
    dstStream.Flush()
End Sub
コードのコピー
private void StreamCopy(Stream dstStream, Stream srcStream)
{
    byte[] buffer= new byte[32768];
    for (;;)
    {
        int read = srcStream.Read(buffer, 0, buffer.Length);
        if (read == 0) break;
        dstStream.Write(buffer, 0, read);
    }
    dstStream.Flush();
}

この関数は、コピーの完了時に Flush メソッドを呼び出して、キャッシュされているデータがあればすべて書き出します。この動作は、圧縮ストリームを処理する際は特に重要です。圧縮ストリームでは、圧縮率を向上させるために大量のデータがキャッシュされるためです。

手順 5:ファイルを展開するコードを追加します。

ファイルの展開]コマンドボタンの Click イベントを処理する次のコードを追加します。

コードのコピー
Private Sub btnExpand_Click(sender As Object, e As EventArgs) Handles btnExpand.Click
    ' アプリケーションディレクトリを取得します。
    Dim appPath As String = Application.ExecutablePath
    Dim i As Integer      = appPath.IndexOf("\bin\")
    If i > 0 Then appPath = appPath.Substring(0, i)
    ' 展開後ファイルのディレクトリを作成します。
    If Directory.Exists(appPath + DIR_EXP) Then
        Directory.Delete(appPath + DIR_EXP, True)
    End If
    Directory.CreateDirectory(appPath + DIR_EXP)
    ' 圧縮統計値を収集する準備をします。
    Dim count As Long
    Dim size As Long
    Dim sizeExpanded As Long
    Dim ticks As Long = DateTime.Now.Ticks
    ' compressed ディレクトリにあるファイルをすべて expanded ディレクトリに展開します。
    Dim srcFile As String
    Dim files As String()
    files = Directory.GetFiles(appPath + DIR_COMP)
    For Each srcFile In files
        ' ファイルを展開します。
        Dim dstFile As String = appPath + DIR_EXP + "\" + Path.GetFileName(srcFile)
        dstFile = dstFile.Replace(".cmp", "")
        ExpandFile(dstFile, srcFile)
        ' 統計値を更新します。
        count = count + 1
        size = size + New FileInfo(srcFile).Length
        sizeExpanded = sizeExpanded + New FileInfo(dstFile).Length
    Next srcFile
    ' 統計値を表示します。
    Dim msg As String
    msg = String.Format("Expanded {0} files in {1} ms." & vbCrLf & "Original size:   {2:#,###}" & vbCrLf & "Expanded size: {3:#,###} ({4:0.00} x size of compressed)", count, (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond, size, sizeExpanded, sizeExpanded / size)
    Label1.Text = msg
End Sub
コードのコピー
private void btnExpand_Click(object sender, EventArgs e)
{
    // アプリケーションディレクトリを取得します。
    string appPath = Application.ExecutablePath;
    int i = appPath.IndexOf(@"\bin\");
    if (i > 0) appPath = appPath.Substring(0, i);
    // 展開後ファイルのディレクトリを作成します。
    if (Directory.Exists(appPath + DIR_EXP))
        Directory.Delete(appPath + DIR_EXP, true);
    Directory.CreateDirectory(appPath + DIR_EXP);
    // 圧縮統計値を収集する準備をします。
    long count = 0;
    long size = 0;
    long sizeExpanded = 0;
    long ticks = DateTime.Now.Ticks;
    // compressed ディレクトリにあるファイルをすべて expanded ディレクトリに展開します。
    foreach (string srcFile in Directory.GetFiles(appPath + DIR_COMP))
    {
        // ファイルを展開します。
        string  dstFile = appPath + DIR_EXP + @"\" + Path.GetFileName(srcFile);
        dstFile = dstFile.Replace(".cmp", "");
        ExpandFile(dstFile, srcFile);
        // 統計値を更新します。
        count++;
        size += new FileInfo(srcFile).Length;
        sizeExpanded += new FileInfo(dstFile).Length;
    }
    // 統計値を表示します。
    string msg = string .Format("Expanded {0} files in {1} ms.\r\n" + "Original size:   {2:#,###}\r\n" + "Expanded size: {3:#,###} ({4:0.00} x size of compressed)", count, (DateTime.Now.Ticks - ticks) / TimeSpan.TicksPerMillisecond, size, sizeExpanded, sizeExpanded / size);
    label1.Text = msg;
}

重要な行は、ユーティリティ関数 ExpandFile の呼び出しです。ここで、前に圧縮されたファイルを展開します。展開後ファイルは、アプリケーションフォルダにある ¥expanded ディレクトリに保存されます。ファイル名は、元のファイルの名前から拡張子 CMP が除去された名前になります。

次に、ExpandFile 関数のコードを示します。

コードのコピー
Private Function ExpandFile(dstFile As String, srcFile As String) As Boolean
    ' ファイルを展開する準備をします。
    Dim retval As Boolean = True
    Dim srcStream As FileStream = Nothing
    Dim dstStream As FileStream = Nothing
    Try
        ' ファイルを開きます。
        srcStream = New FileStream(srcFile, FileMode.Open, FileAccess.Read)
        dstStream = New FileStream(dstFile, FileMode.Create, FileAccess.Write)
        ' 圧縮ソースのエキスパンダストリームを開きます。
        Dim sr As C1ZstreamReader = New C1ZStreamReader(srcStream)
        ' エキスパンダストリームを出力先のファイルにコピーします。
        StreamCopy(dstStream, sr)
    Catch
        ' 例外? 呼び出し元に処理が失敗したことを通知します。
        retval = False
    Finally
        ' 必ずストリームを閉じます。
        If Not (srcStream Is Nothing) Then srcStream.Close()
        If Not (dstStream Is Nothing) Then dstStream.Close()
    End Try
  ' 完了。
  ExpandFile = retval
End Sub
コードのコピー
{
    // ファイルを展開する準備をします。
    bool retval = true;
    FileStream srcStream = null;
    FileStream dstStream = null;
    try
    {
        // ファイルを開きます。
        srcStream = new FileStream(srcFile, FileMode.Open, FileAccess.Read);
        dstStream = new FileStream(dstFile, FileMode.Create, FileAccess.Write);
        // 圧縮ソースのエキスパンダストリームを開きます。
        C1ZStreamReader sr = new C1ZStreamReader(srcStream);
        // エキスパンダストリームを出力先のファイルにコピーします。
        StreamCopy(dstStream, sr);
    }
    catch
    {
        // 例外? 呼び出し元に処理が失敗したことを通知します。
        retval = false;
    }
    finally
    {
        // 必ずストリームを閉じます。
        if (srcStream != null) srcStream.Close();
        if (dstStream != null) dstStream.Close();
    }
    // 完了。
    return retval;
}

この関数は CompressFile に似ていますが、C1ZStreamWriter を出力先ストリームにアタッチする代わりに、C1ZStreamReader をソースストリームにアタッチする点が異なります。

これで「ファイルの圧縮」チュートリアルは終了です。