WF でファイルやフォルダ操作を行う上で必要性を非常に感じているのが Zip 圧縮。バッチスクリプト等での定型処理でも結構使われているのではないでしょうか。
これを実際に .NET で行おうとすると何種類かの選択肢から方式を選ぶことになると思います。
- J# ライブラリを利用する
- DotNetZip 等の外部ライブラリを利用する
- Windows 標準機能を利用する
自分が今まで利用していたのは(1)の方法で、これだと MS 純正コンポーネントのみで済み、かつ結構楽な手段でサンプルもごろごろ転がっていたと思います。ところが、.NET 4 から J# を利用するというのが結構面倒でそのままでは利用できないため WF から利用するのは躊躇していました。
そうなると(2)か(3)なのですが、(2)のように外部ライブラリを利用するのは最も楽で安定するのですが、気持ちとして参照 Dll がどんどん増えていく事が嫌いというのもあり却下。必然的に(3)の方法となってしまいます。
Windows は XP 以降、OS の標準機能として Zip 圧縮と解凍をサポートしています。それを利用するためには COM オブジェクトを通して機能を呼び出してあげる必要があります。既に VBS や VB.NET でのロジックを作成されていたサイトがあったので、そこを参考にしてみました。
1: Imports System.Activities
2:
3: Public Class ZipCompressActivity
4: Inherits AsyncCodeActivity
5:
6: ''' <summary>空 Zip 書庫のバイナリ</summary>
7: Private EMPTYZIP_ARRAY As Byte() = {&H50, &H4B, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
8:
9: Public Property ArchiveFileName As InArgument(Of String)
10: Public Property TargetFileName As InArgument(Of String)
11: Public Property Results As OutArgument(Of Boolean)
12:
13: Private Delegate Function asyncCompress(ByVal archive As String, ByVal target As String) As Boolean
14:
15: Protected Overrides Function BeginExecute(context As System.Activities.AsyncCodeActivityContext, callback As System.AsyncCallback, state As Object) As System.IAsyncResult
16: Dim archive = context.GetValue(Me.ArchiveFileName)
17: Dim target = context.GetValue(Me.TargetFileName)
18: archive = IO.Path.GetFullPath(archive)
19: target = IO.Path.GetFullPath(target)
20:
21: Dim asyncComp = New asyncCompress(AddressOf CompressFile)
22: context.UserState = asyncComp
23:
24: Return asyncComp.BeginInvoke(archive, target, callback, state)
25: End Function
26:
27: Protected Overrides Sub EndExecute(context As System.Activities.AsyncCodeActivityContext, result As System.IAsyncResult)
28: Dim asyncExecute = TryCast(context.UserState, asyncCompress)
29: Dim compResult = asyncExecute.EndInvoke(result)
30:
31: context.SetValue(Me.Results, compResult)
32: End Sub
33:
34: Private Function CompressFile(ByVal archive As String, ByVal target As String) As Boolean
35: Dim result As Boolean = False
36: If Not IO.File.Exists(archive) Then Me.CreateEmptyZip(archive)
37:
38: Dim shl As Object = Nothing
39: Dim zipFolder As Object = Nothing
40: Dim zipFolderFilesCount As Object = Nothing
41: Dim targetItem As Object = Nothing
42: Try
43: shl = CreateObject("Shell.Application")
44: zipFolder = shl.NameSpace(IO.Path.GetFullPath(archive))
45: zipFolderFilesCount = zipFolder.Items().Count
46: targetItem = shl.NameSpace(IO.Path.GetFullPath(target & "\..")).ParseName(IO.Path.GetFileName(target))
47:
48: zipFolder.CopyHere(targetItem)
49: Do Until zipFolder.Items().Count > zipFolderFilesCount
50: System.Threading.Thread.Sleep(500)
51: Loop
52: result = True
53: Catch ex As Exception
54: Finally
55: targetItem = Nothing
56: zipFolderFilesCount = Nothing
57: zipFolder = Nothing
58: shl = Nothing
59: End Try
60:
61: Return result
62: End Function
63:
64: ''' <summary>Zip 空書庫の作成</summary>
65: Private Sub CreateEmptyZip(ByVal archive As String)
66: Using fs As New IO.FileStream(archive, IO.FileMode.Create)
67: fs.Write(EMPTYZIP_ARRAY, 0, EMPTYZIP_ARRAY.Length)
68: End Using
69: End Sub
70:
71: End Class
ソースとしてはこのような感じでアクティビティにできました。注意する点は、COM オブジェクトを呼び出しているので遅延バインディングを利用しています。参照設定をかけて利用していませんので、タイプミスは実行時例外になります。また、途中で shl.NameSpace(~) のように呼び出しを行っていますが、ここも対象となるパスを渡すのですが String を渡した場合は結果が Null (Nothing) になってしまうので、なんらかのメソッドの戻り値を直接渡す必要がある、というのが気を付けるところだと思います。
アクティビティの機能としては、指定された 1 ファイルを圧縮する、というものですので複数ファイルを圧縮する際は ForEach アクティビティなどの子供として利用してあげればいいかと思います。
0 件のコメント:
コメントを投稿