原创 用DeflateStream和GZipStream压缩数据

2011-1-10 08:31 5448 12 13 分类: 软件与OS

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


 


 

文章评论1条评论)

登录后参与讨论

用户398633 2011-3-21 17:07

不大明白下面语句的含义: NewBuffer(10 + FileNameBuffer.Length) = 0 '文件名尾部 为什么文件名的尾部数据设置为0呢? NewBuffer(3) = 8 设置值为8时什么意思呢? 万望不吝赐教。
相关推荐阅读
用户1694343 2012-01-05 14:03
单色LCD点阵字模提取工具
单色LCD点阵字模提取工具...
用户1694343 2011-06-05 08:42
点阵汉字库生成器
  本程序是本人第一个用Microsoft C#开的第一个软件。希望能为做嵌入式的朋友带来方便。 支持以下点阵:  12点阵  16点阵支持以下取模式方式:  逐行扫描  行列扫描  逐列扫描  列行...
用户1694343 2011-03-16 21:58
图形点阵提取工具
...
用户1694343 2011-01-08 22:58
获取文件关联的图标
System.Drawing.Icon名字空间提供了一个非常简单的办法获取与文件相关联的图标,调用ExtractAssociatedIcon方法,指定文件路径就能得到一个ICON对象.此方法返回指定文...
用户1694343 2011-01-08 22:58
Outline示例代码
1.三级树Outline1->Lines->Add("Root"); for(int i=0;i<5;i++) {  int m;   m=Outline1->AddChild...
我要评论
1
12
关闭 站长推荐上一条 /2 下一条