|
摘要 如果你的應用程序從未使用過壓縮,那么你很幸運。而對于另一部分使用壓縮的開發(fā)人員來說,好消息是,.NET 2.0如今提供了兩個類來處理壓縮和解壓問題。本文正是想討論何時以及如何使用這些有用的工具。
引言
.NET框架2.0中的一個新名稱空間是System.IO.Compression。這個新名稱空間提供了兩個數(shù)據(jù)壓縮類:DeflateStream和GZipStream。這兩個壓縮類都支持無損壓縮和解壓,其設計目的是為了處理流式數(shù)據(jù)的壓縮和解壓問題。
壓縮是減少數(shù)據(jù)大小的有效辦法。例如,如果你有巨大量的數(shù)據(jù)存儲在你的SQL數(shù)據(jù)庫中,那么如果你在把這些數(shù)據(jù)保存到一個表之前壓縮一下,你就可以節(jié)省大量的磁盤空間。而且,既然現(xiàn)在你把更小塊的數(shù)據(jù)保存到你的數(shù)據(jù)庫中,花費在磁盤I/O方面的操作將會大大減少。壓縮的缺點是,它要求你的機器進行另外的處理(因此需要另外的處理時間),并且,在你決定把壓縮應用于你的程序之前,你需要計算這一部分時間。
壓縮在你需要在網(wǎng)上傳送數(shù)據(jù)的情況中是極其有用的,特別是對于非常慢且代價昂貴的網(wǎng)絡,例如GPRS連接。在這種情況中,使用壓縮能夠極大地縮小數(shù)據(jù)尺寸并且減少整個通訊耗費。Web服務是另一個領域-此時,使用壓縮能提供巨大的優(yōu)點,因為XML數(shù)據(jù)能被高度壓縮。
但是一旦你認為程序的性能代價值得使用壓縮,那么你將需要深入地理解.NET 2.0的兩個新的壓縮類,而這正是我想在本文中所闡述的。
創(chuàng)建示例應用程序
在本文中,我將構建一個示例應用程序來展示壓縮的使用。該應用程序允許你壓縮文件,包括普通文本文件。然后,你能夠把該示例中的代碼重用于你自己的應用程序中。
首先,使用Visual Studio 2005創(chuàng)建一個新的Windows應用程序并且使用下列控件來填充默認的表單(見圖1):

圖1.填充表單:使用所有顯示的控件填充默認的Form1。
? GroupBox控件
? RadioButton控件
? TextBox控件
? Button控件
? Label控件
切換到Form1的code-behind并且導入下列名稱空間:
Imports System.IO
Imports System.IO.Compression
在你開始使用壓縮類前,理解其工作原理是非常重要的。這些壓縮類從一個字節(jié)數(shù)組中讀取數(shù)據(jù),壓縮它并且把結果存儲到一個流對象中。對于解壓來說,解壓存儲到一個流對象中的數(shù)據(jù),然后把它存儲到另一個流對象中。
首先,定義Compress()函數(shù),它有兩個參數(shù):algo和data。第一個參數(shù)指定使用哪種算法(GZip或Deflate);第二個參數(shù)是一個包含要壓縮的數(shù)據(jù)的字節(jié)數(shù)組。一個內(nèi)存流對象將被用來存儲壓縮數(shù)據(jù)。一旦壓縮完成,你需要計算壓縮比,這是用壓縮的數(shù)據(jù)的大小除以解壓數(shù)據(jù)的大小計算的。
然后,存儲在內(nèi)存流中的壓縮的數(shù)據(jù)被復制到另一個字節(jié)數(shù)組中并且被返回到調(diào)用函數(shù)。另外,你還要使用一個StopWatch對象來跟蹤該壓縮算法使用了多少時間。Compress()函數(shù)定義如下:
Public Function Compress(ByVal algo As String, ByVal data() As Byte) As Byte()
Try
Dim sw As New Stopwatch
'---ms用于存儲壓縮的數(shù)據(jù)---
Dim ms As New MemoryStream()
Dim zipStream As Stream = Nothing
'---開始秒表計時---
sw.Start()
If algo = "Gzip" Then
zipStream = New GZipStream(ms, CompressionMode.Compress, True)
ElseIf algo = "Deflate" Then
zipStream = New DeflateStream(ms, CompressionMode.Compress, True)
End If
'---使用存儲在數(shù)據(jù)中的信息進行壓縮---
zipStream.Write(data, 0, data.Length)
zipStream.Close()
'---停止秒表---
sw.Stop()
'---計算壓縮比---
Dim ratio As Single = Math.Round((ms.Length / data.Length) * 100, 2)
Dim msg As String = "Original size: " & data.Length & _
", Compressed size: " & ms.Length & _
", 壓縮比: " & ratio & "%" & _
", Time spent: " & sw.ElapsedMilliseconds & "ms"
lblMessage.Text = msg
ms.Position = 0
'---用來存儲壓縮了的數(shù)據(jù)(字節(jié)數(shù)組)---
Dim c_data(ms.Length - 1) As Byte
'---把內(nèi)存流的內(nèi)容讀取到字節(jié)數(shù)組---
ms.Read(c_data, 0, ms.Length)
Return c_data
Catch ex As Exception
MsgBox(ex.ToString)
Return Nothing
End Try
End Function
這個Decompress()函數(shù)將解壓由Compress()函數(shù)壓縮的數(shù)據(jù)。第一個參數(shù)指定要使用的算法。包含壓縮的數(shù)據(jù)的字節(jié)數(shù)組被作為第二個參數(shù)傳遞,然后它被復制到一個內(nèi)存流對象中。然后,這些壓縮類將解壓存儲在內(nèi)存流中的數(shù)據(jù),然后把解壓的數(shù)據(jù)存儲到另一個流對象中。為了獲得解壓的數(shù)據(jù),你需要讀取來自流對象的數(shù)據(jù)。這是通過使用RetrieveBytesFromStream()函數(shù)來實現(xiàn)的(將在后面解釋)。
Decompress()函數(shù)的定義如下所示:
Public Function Decompress(ByVal algo As String, ByVal data() As Byte) As Byte()
Try
Dim sw As New Stopwatch
'---復制數(shù)據(jù)(壓縮的)到ms---
Dim ms As New MemoryStream(data)
Dim zipStream As Stream = Nothing
'---開始秒表---
sw.Start()
'---使用存儲在ms中的數(shù)據(jù)解壓---
If algo = "Gzip" Then
zipStream = New GZipStream(ms, CompressionMode.Decompress)
ElseIf algo = "Deflate" Then
zipStream = New DeflateStream(ms, CompressionMode.Decompress, True)
End If
'---用來存儲解壓的數(shù)據(jù)---
Dim dc_data() As Byte
'---解壓的數(shù)據(jù)存儲于zipStream中;
'把它們提取到一個字節(jié)數(shù)組中---
dc_data = RetrieveBytesFromStream(zipStream, data.Length)
'---停止秒表---
sw.Stop()
lblMessage.Text = "Decompression completed. Time spent: " & _
sw.ElapsedMilliseconds & "ms" & _
", Original size: " & dc_data.Length
Return dc_data
Catch ex As Exception
MsgBox(ex.ToString)
Return Nothing
End Try
End Function
這個RetrieveBytesFromStream()函數(shù)使用了兩個參數(shù):一個流對象,一個整數(shù),并返回一個包含解壓的數(shù)據(jù)的字節(jié)數(shù)組。這個整數(shù)參數(shù)用于決定每次把多少個字節(jié)從該流對象中讀取到字節(jié)數(shù)組中。這是必要的,因為當數(shù)據(jù)被解壓時,你不知道存在于流對象中的解壓數(shù)據(jù)的大小。因此,有必要動態(tài)地把字節(jié)數(shù)組擴展成塊以便存儲在運行時刻期間解壓縮的數(shù)據(jù)中。當你不斷地擴展字節(jié)數(shù)組時,塊太大會浪費內(nèi)存,而塊太小則會失去珍貴的時間。因此,可以由調(diào)用例程來決定要讀取的最佳塊大小。
RetrieveBytesFromStream()函數(shù)的定義如下:
Public Function RetrieveBytesFromStream( _
ByVal stream As Stream, ByVal bytesblock As Integer) As Byte()
'---從一個流對象中檢索字節(jié)---
Dim data() As Byte
Dim totalCount As Integer = 0
Try
While True
'---逐漸地增加數(shù)據(jù)字節(jié)數(shù)組-的大小--
ReDim Preserve data(totalCount + bytesblock)
Dim bytesRead As Integer = stream.Read(data, totalCount, bytesblock)
If bytesRead = 0 Then
Exit While
End If
totalCount += bytesRead
End While
'---確保字節(jié)數(shù)組正確包含提取的字節(jié)數(shù)---
ReDim Preserve data(totalCount - 1)
Return data
Catch ex As Exception
MsgBox(ex.ToString)
Return Nothing
End Try
End Function
注意,在Decompress()函數(shù)中,你調(diào)用了RetrieveBytesFromStream()函數(shù),如下所示:
dc_data = RetrieveBytesFromStream(zipStream, data.Length)
塊大小是指壓縮的數(shù)據(jù)的大小(data.length)。在大多數(shù)情況中,解壓縮的數(shù)據(jù)要比壓縮的數(shù)據(jù)大幾倍(由壓縮比所顯示),因此,在運行時刻期間你將至多動態(tài)地擴展字節(jié)數(shù)組幾倍。作為一個例子,假定壓縮比是百分之20而壓縮的數(shù)據(jù)的大小為2MB,那么,在這種情況中,解壓的數(shù)據(jù)將是10MB。因此,該字節(jié)數(shù)組將被動態(tài)地擴展5倍。理想情況下,在運行時刻期間該字節(jié)數(shù)組不應該被擴展太頻繁,因為這將會嚴重地減慢應用程序運行速度。但是使用壓縮的數(shù)據(jù)的大小作為塊大小確是一種好的辦法。
AspNet技術:用.NET 2.0壓縮/解壓功能處理大型數(shù)據(jù),轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。