您的位置:首页 > 其它

用DeflateStream和GZipStream压缩数据

2016-05-31 10:18 381 查看



System.IO.Compression 命名空间提供两个类:DeflateStream和GZipStream,这两个类都可以实现数据压缩.这两个类采用都采用Deflate算法来进行无损数据压缩,

下面通过简单的代码来比较两者的差别.Deflate算法的信息,可以从官网http://www.faqs.org/rfcs/rfc1951.html和维基百科

http://zh.wikipedia.org/zh-cn/DEFLATE得到.

以下Compress函数通过参数UseDeflateStream来指定用哪个流来生成文件,分别生成以".de"和".gs"为扩展名的文件.

Private Function Compress(ByVal FileName As String, ByVal UseDeflateStream As Boolean) As Boolean

Dim CompressedLen As Integer

Dim Buffer() As Byte = My.Computer.FileSystem.ReadAllBytes(FileName)

Dim ms As New MemoryStream()

If UseDeflateStream Then

Dim cs As DeflateStream

cs = New DeflateStream(ms, CompressionMode.Compress, True)

cs.Write(Buffer, 0, Buffer.Length)

cs.Close()

cs.Dispose()

Else

Dim cs As GZipStream

cs = New GZipStream(ms, CompressionMode.Compress, True)

cs.Write(Buffer, 0, Buffer.Length)

cs.Close()

cs.Dispose()

End If

CompressedLen = ms.Length

Dim NewBuffer() As Byte

ReDim NewBuffer(ms.Length - 1)

ms.Position = 0

ms.Read(NewBuffer, 0, CompressedLen)

ms.Close()

ms.Dispose()

Dim fInfo As New FileInfo(FileName)

Dim NewFileName As String = fInfo.FullName.Replace(fInfo.Extension, IIf(UseDeflateStream, ".de", ".gs"))

My.Computer.FileSystem.WriteAllBytes(NewFileName, NewBuffer, False)

End Function

Private Sub btnbtnCompress_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCompress.Click

Compress("C:\Users\Kuge\Documents\Temp\log.txt", True)

Compress("C:\Users\Kuge\Documents\Temp\log.txt", False)

End Sub

通过比较,GZipStream生成的文件比DeflateStream生成的文件多了18字节的数据,其中10字节位于文件头部,8字节位于文件尾部,这18字节研究是

用来干什么的呢?我们来看看GZIP的文件格式(http://www.gzip.org/zlib/rfc-gzip.html).

原来,GZipStream采用DeflateStream一模一样的算法,仅仅是在流的头部增加".gz"文件的文件头信息共10个字节,以及尾部的原始文件(压缩前)

的大小和原始文件的CRC32校验值,中间的压缩数据部分完全一致,如图所示,左边为调用"DeflateStream类"生成的文件,左边为调用"GZipStream类"

生成的文件,



+---+---+---+---+---+---+---+---+---+---+

|ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)

+---+---+---+---+---+---+---+---+---+---+

参照GZIP格式文档,文件开头1FH,8BH为GZ格式文件的标记ID1,ID2,08H为压缩模式CM等,以此类推;文件结尾的353A7F57H

则表示原始文件的CRC校验值,此值可以借助WINRAR等压缩软件得到,也可以编写程序计算,官方推荐CRC算法代码,0000047F则

表示原始文件的大小为1151字节.

可见,System.IO.Compression 命名空间的两个类:DeflateStream和GZipStream其用途是不同的.DeflateStream适合于

通信传输压缩,或者自定义文件格式的场合,其数据流仅仅是压缩后的数据流,不包含任何文件类型信息和原始数据文件大小及

校验信息;而GZipStream则在流的头部加入了GZIP格式文件的头,以及原始文件校验值,原始文件大小信息,将流保存成文件可以

被流行的压缩软件WINRAR,7-ZIP,WINZIP等解压,非常方便.

事实上,GZipStream缺少原始文件名的信息,我们可以修改文件头标志,然后在文件头后面再增加若干字节,用于存储文件名

,这样压缩出来的软件在被WINRAR解压之后,就能还原出原始文件名了.以下是示例代码,供参考.注意,此实例不适合体积大的文件和

压缩之后的文件.

Private Function CompressByGZipStream(ByVal FileName As String) As Boolean

Dim i As Integer

Dim CompressedLen As Integer

Dim Buffer() As Byte = My.Computer.FileSystem.ReadAllBytes(FileName)

Dim ms As New MemoryStream()

Dim cs As GZipStream

cs = New GZipStream(ms, CompressionMode.Compress, True)

cs.Write(Buffer, 0, Buffer.Length)

cs.Close()

cs.Dispose()

CompressedLen = ms.Length

Dim FileNameSize As Integer

Dim FileNameBuffer() As Byte

Dim fInfo As New FileInfo(FileName)

FileNameBuffer = System.Text.Encoding.Default.GetBytes(fInfo.Name)

FileNameSize = FileNameBuffer.Length + 1

Dim NewBuffer() As Byte

ReDim NewBuffer(FileNameSize + ms.Length - 1)

ms.Position = 0

ms.Read(NewBuffer, 0, 10)

For i = 0 To FileNameBuffer.Length - 1

NewBuffer(10 + i) = FileNameBuffer(i)

Next

NewBuffer(10 + FileNameBuffer.Length) = 0 '文件名尾部

NewBuffer(3) = 8

ms.Read(NewBuffer, FileNameSize + 10, CompressedLen - 10)

ms.Close()

ms.Dispose()

Dim NewFileName As String = fInfo.FullName.Replace(fInfo.Extension, ".gz")

My.Computer.FileSystem.WriteAllBytes(NewFileName, NewBuffer, False)

End Function



MS .NET Framework 4.0 压缩数据更加方便,可以将输入流通过copyto文法,直接输出到压缩流,然后通过输出流输出到文件,只需要若干行代码即可完成压缩大文件的整个过程.MS .NET Framework 4.0 的代码如下所示:

输入流-->压缩流-->输出流

''' <summary>

''' 输入流-->压缩流-->输出流

''' </summary>

''' <param name="FileName"></param>

''' <remarks></remarks>

Private Sub CompressToGZip(ByVal FileName As String)

Dim InputFileInfo As New FileInfo(FileName)

Dim OutputFileInfo As New FileInfo(FileName.Replace(InputFileInfo.Extension, ".gz"))

Using 输入文件流 As FileStream = InputFileInfo.OpenRead

Using 输出文件流 As FileStream = File.Create(OutputFileInfo.FullName)

Using 压缩流 As New System.IO.Compression.GZipStream(输出文件流, Compression.CompressionMode.Compress, True)

输入文件流.CopyTo(压缩流) '从输入文件流读取所有字节并写到压缩流,由输出流输出到文件

End Using

End Using

End Using

End Sub
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: