关闭

用DeflateStream和GZipStream压缩数据

122人阅读 评论(0) 收藏 举报
分类:


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类"
生成的文件,
 

 

98d84d39-8b39-40c5-8ffb-bdb45a806e03.JPG

 

+---+---+---+---+---+---+---+---+---+---+
|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

 


 9d1aa83c-40dd-41e1-8525-8948d9d1dd1b.JPG

 


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

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:218578次
    • 积分:4024
    • 等级:
    • 排名:第7745名
    • 原创:89篇
    • 转载:548篇
    • 译文:13篇
    • 评论:7条
    最新评论