wp7/tips

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

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

Silverlight SharpZipLib http://slsharpziplib.codeplex.com/

この Silverlight SharpZipLib の大きな特徴は Silverlight(WP) 特有の IsolatedStorage に対応している点です。 普通 SharpZipLib で ZIP 圧縮する場合は FastZip クラスを使用しますが、このライブラリでは IsolatedFastZip クラスを使用します。

しかしながらこの 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 が含まれないように、ファイルフィルタを指定しておきます。

	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 クラスを使用すると、指定したフォルダやファイルだけを圧縮することが可能です。

	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行, 235行目あたりにコードを追加する

	
	// 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 が出来ていたら完成。