WP TIPS に戻る

WP7 で SharpZipLib を使って圧縮解凍する

WP7 では Zip ファイルの圧縮解凍クラスは標準で用意されていませんが、有名ZIPライブラリ SharpZipLib の WP7 対応版が codeplex 上に公開されていて、IsolatedStorage 上で SharpZipLib を使用することが出来ます。

しかしながらこの SharpZipLib は Silverlight 3/4 及び Windows Phone7 対応とされていますが、WP7.5(Mango) では

  1. 日本語ファイル名・フォルダ名が化ける
  2. FastZip クラスでファイル・フォルダ名の指定方法がおかしく圧縮が出来ない
  3. ルートフォルダ以下を圧縮しようとすると現在作成中の圧縮ファイルも圧縮しようとしてしまう
    などのバグがあります(2011/11/17 現在 SharpZipLib.Silverlight 0.86.0.518. の場合 )

というわけでソースコードをちょっといじってみたら簡単に動作しました。

使い方

SharpZipLib.WindowsPhone7.dll をダウンロードし、プロジェクトの参照に追加します。

FastZip による一括 Zip

IsolatedFastZip クラスを使用すると、指定したフォルダ以下のファイルやフォルダを一括して圧縮することが出来ます。
4番目, 5番目にファイル名、フォルダ名のフィルタを指定しておくことで、指定したファイルやフォルダだけを圧縮することが可能です。

public void FastZip()
{
   string zipname = "FastZip.zip";

   IsolatedFastZip fz = new IsolatedFastZip();
   fz.IgnoreSharedFolder = true;
   fz.CreateEmptyDirectories = true;
   fz.CreateZip(zipname, @"Log", true, @"", "");
}

ルートフォルダ以下を圧縮する時は注意が必要です。
というのも FastZip が作成する Zip 自体を圧縮しようとしてしまい、例外が発生してしまいます。
この場合は Zip が含まれないように、.zip 以外に格納したいファイルの拡張子をファイルフィルタを指定しておきます。
本当なら「無視するファイル名」というパラメータでも用意しておけば良いんですけれども。まぁこれで我慢しましょう。

public void FastZip()
{
   string zipname = "FastZip.zip";

   IsolatedFastZip fz = new IsolatedFastZip();
   fz.IgnoreSharedFolder = true;
   fz.CreateEmptyDirectories = true;
   fz.CreateZip(zipname, @"", true, @"\.txt$;\.html$;\.xml$;\.dat$;\.cgi$;\.jpg$;", "");
}

FastZip による一括展開

IsolatedFastZip クラスで一括展開も可能です。
Extract.zip をルートフォルダに展開するには以下のように行います。
IsolatedFastZip クラスのコンストラクタに FastZipEvents インスタンスを渡しておくことで、進捗も取得可能です。

public void Extract()
{
   IsolatedFastZip fastZip = new IsolatedFastZip();
   fastZip.CreateEmptyDirectories = true;
   fastZip.IgnoreSharedFolder = true;
   fastZip.ExtractZip("Extract.zip", "", "");
}

ZipFile による個別 Zip

IsolatedZipFile クラスを使用すると、指定したフォルダやファイルだけを圧縮することが可能です。
AddDirectory メソッドで指定するフォルダ名は "/" を最後に付けてあげて下さい。そうしないとファイルとして Zip に格納されてしまいます。結構はまります。

string zipname = "Zip_Singlefile.zip";

IsolatedZipFile f = IsolatedZipFile.Create(zipname);
f.BeginUpdate();
f.Add("settings.xml");
f.AddDirectory("Cache/");
f.AddDirectory("Log/");
f.AddDirectory(@"Log/AccessLog/");
f.Add(@"Log/AccessLog/test.txt");
f.CommitUpdate();
f.Close();

ビルドの方法

以下は、Silverlight SharpZipLib を WP7.5 で使用するためのメモです。

  1. ダウンロード
    Silverlight SharpZipLib (http://slsharpziplib.codeplex.com/) プロジェクトのページから "Source Code" を選択し、Change Set 75568 をダウンロードします。
  2. 適当なフォルダに展開します。
  3. trunk\SharpZipLib.Silverlight.sln を開きます。
  4. ソリューションの一部が開きませんというメッセージが出るが OK ボタンを押す。
  5. 以下のプロジェクトをアンロードする。
    • QuickStart.SL3
    • QuickStart.SL4
    • QuickStarts.Web
    • SharpZipLib.Silverlight.Tests_NUnit3
    • SharpZipLib.Silverlight3
    • SharpZipLib.Silverlight.Tests_NUnit4
    • SharpZipLib.Silverlight.Tests_Silverlight4
    • SharpZipLib.Silverlight.Tests_Silverlight4_OOB
    • SharpZipLib.Silverlight4
      開いているプロジェクトは、以下の3点だけ。
    • QuickStart.Phone7
    • SharpZipLib.WindowsPhone7
    • SharpZipLib.WindowsPhone7.Tests_Silverlight
  6. 残っている3つのプロジェクトを "Windows Phone 7.1 にアップグレード" する。
  7. SharpZipLib.WindowsPhone7\IsolatedStorage\IsolatedZipEntryFactory.cs の 192行目あたりにコードを追加する
    // 2011/11/17 mikiofuku 追加
    // 元々のソースでは、以下の行で UnicodeText をクリアしているが、
    // 日本語文字列が化けてしまうので、強制的に unicode を有効にした。
    result.IsUnicodeText = true;
    
    // ここまで // 
    
  8. SharpZipLib.WindowsPhone7\IsolatedStorage\IsolatedFileSystemScanner.cs の 56行, 216行, 236行,278 行目あたりを変更する
    /// <summary>
    /// Shared フォルダを無効にするかどうか。2011/11/17 mikiofuku 追加
    /// </summary>
    public bool IgnoreSharedFolder;
    
    ~中略~
    
    try
    {
       // 2011/11/17 mikiofuku 追加
       if (this.IgnoreSharedFolder && directory.Equals("Shared"))
           return;
           
       string[] names = store.GetFileNames(Path.Combine(directory, "*.*"));
    
    ~中略~
    
    if (fileName != null)
    {
       // 2011/11/17 mikiofuku 追加
       // 元々のソースでは OnProcessFile(fileName) に渡されている fileName が
       // ファイル名だけでパスが含まれておらず、FileNotFound 例外が発生していた。
       // そのためディレクトリ名を付加した。
       string n = Path.Combine(directory, fileName);
       OnProcessFile(n);
                                   
       // OnProcessFile(fileName);
       
       // ここまで // 
    
       if (!alive_)
    
    ~中略~
    
    if ((directoryFilter_ == null) || (directoryFilter_.IsMatch(fulldir)))
    {
    
       // 2011/11/17 mikiofuku 追加
       // 元々のソースでは ScanDir(fulldir, true) に渡されている fulldir が
       // 現在のフォルダ名だけで上位のフォルダ名が含まれておらず、DirectoryNotFound 例外が発生していた。
       // そのため上位のディレクトリ名を付加した。
       string d = Path.Combine(directory, fulldir);
       ScanDir(d, true);
       
       //  ScanDir(fulldir, true);
       
       // ここまで // 
    
  9. SharpZipLib.WindowsPhone7\IsolatedStorage\IsolatedFileZip.cs の 188行, 259行目あたりを変更する
    /// <summary>
    /// Shared フォルダを無効にするかどうか。2011/11/17 mikiofuku 追加
    /// </summary>
    public bool IgnoreSharedFolder;
    
    ~中略~
    
    outputStream_.UseZip64 = UseZip64;
    var scanner = new IsolatedFileSystemScanner(fileFilter, directoryFilter);
    
    // 2011/11/17 mikiofuku 追加
    scanner.IgnoreSharedFolder = this.IgnoreSharedFolder;
  10. 後はビルドを行い SharpZipLib.WindowsPhone7.dll が出来ていたら完成。