SharpZipLib是一个非常优秀的.NET环境下的ZIP文档压缩与解压组件,免费且开源(MIT授权)。

本篇文章介绍一下如何使用该组件压缩一个文件夹中的所有文件到ZIP文档、解压一个ZIP文档到文件夹,以及如何利用SharpZipLib在内存中动态的加解压数据。

先下载最新版的SharpZipLib https://github.com/icsharpcode/SharpZipLib

然后使用下面的代码:

Imports System.IO
Imports System.Text

Imports ICSharpCode
Imports ICSharpCode.SharpZipLib
Imports ICSharpCode.SharpZipLib.Zip
Imports ICSharpCode.SharpZipLib.Zip.Compression
Imports ICSharpCode.SharpZipLib.Core


Public Class ClsoZip

    '''<summary>加压数据</summary>
    '''<param name="data">数据</param>
    '''<param name="offset">起始偏移</param>
    '''<param name="length">长度</param>
    '''<param name="level">压缩级别</param>
    '''<param name="noheader">是否不包含ZIP文档头部信息</param>
    Public Shared Function Compress(ByVal data As Byte(), Optional ByVal offset As Integer = 0, Optional ByVal length As Integer = -1, Optional ByVal level As Integer = 9, Optional ByVal noheader As Boolean = False) As Byte()

        If data Is Nothing OrElse data.Length = 0 Then Return Nothing
        If offset < 0 Then offset = 0
        If length <= 0 Then length = data.Length
        If level <= 0 Then level = 1
        If level > 9 Then level = 9

        Dim ms As New MemoryStream
        Dim def As New Deflater(level, noheader)
        def.SetInput(data, offset, length)
        def.Finish() '以前遗忘了这一步,导致无限循环BUG

        Dim output(&H800 - 1) As Byte
        Do Until (def.IsFinished)
            Dim count As Integer = def.Deflate(output)
            If count = 0 Then Exit Do '再加一个安全检测
            ms.Write(output, 0, count)
        Loop

        Dim ret As Byte() = ms.ToArray
        ms.Close()
        Return ret
    End Function

    '''<summary>加压字符</summary>
    '''<param name="str">字符</param>
    '''<param name="enc">编码</param>
    '''<param name="level">压缩级别</param>
    '''<param name="noheader">是否不包含ZIP文档头部信息</param>
    Public Shared Function CompressString(ByVal str As String, Optional ByVal enc As Encoding = Nothing, Optional ByVal level As Integer = 9, Optional ByVal noheader As Boolean = False) As Byte()
        If enc Is Nothing Then enc = Encoding.Default
        Dim data As Byte() = enc.GetBytes(str)
        Return Compress(data, , , level, noheader)
    End Function

    '''<summary>解压数据</summary>
    '''<param name="data">数据</param>
    '''<param name="noheader">是否不包含ZIP文档头部信息</param>
    Public Shared Function Decompress(ByVal data As Byte(), Optional ByVal noheader As Boolean = False) As Byte()
        If data Is Nothing OrElse data.Length < 1 Then Return Nothing

        Dim ms As New MemoryStream
        Dim inf As New Inflater(noheader)
        inf.SetInput(data)

        Dim buffer(&H800 - 1) As Byte
        Do Until (inf.IsFinished)
            Dim count As Integer = inf.Inflate(buffer)
            If count = 0 Then Exit Do '再加一个安全检测
            ms.Write(buffer, 0, count)
        Loop

        Dim ret As Byte() = ms.ToArray
        ms.Close()

        Return ret
    End Function

    '''<summary>解压字符</summary>
    '''<param name="data">数据</param>
    '''<param name="enc">编码</param>
    '''<param name="noheader">是否不包含ZIP文档头部信息</param>
    Public Shared Function DecompressString(ByVal data As Byte(), Optional ByVal enc As Encoding = Nothing, Optional ByVal noheader As Boolean = False) As String
        If enc Is Nothing Then enc = Encoding.Default
        Dim bs As Byte() = Decompress(data, noheader)
        Return enc.GetString(bs)
    End Function


    '''<summary>快速加压</summary>
    Public Shared Sub FastCreateZip(ByVal zipPath As String, ByVal folderPath As String, Optional ByVal fileFilter As String = "", Optional ByVal password As String = Nothing)
        Dim fz As New FastZip
        If password <> Nothing Then fz.Password = password
        fz.CreateZip(zipPath, folderPath, True, fileFilter)
    End Sub

    '''<summary>快速解压</summary>
    Public Shared Sub FastExtractZip(ByVal zipPath As String, ByVal folderPath As String, Optional ByVal fileFilter As String = "", Optional ByVal password As String = Nothing)
        Dim fz As New FastZip
        If password <> Nothing Then fz.Password = password
        fz.ExtractZip(zipPath, folderPath, fileFilter)
    End Sub


    '''<summary>解压ZIP文件到目录</summary>
    '''<param name="zipPath">ZIP文件路径</param>
    '''<param name="folderPath">目标文件夹</param>
    '''<param name="password">密码</param>
    '''<param name="overwrite">是否覆盖文件</param>
    Public Shared Sub DecompressZip(ByVal zipPath As String, ByVal folderPath As String, Optional ByVal password As String = Nothing, Optional ByVal overwrite As Boolean = True)
        ' 初始化文档、密码、目标文件夹路径
        Dim z As New ZipInputStream(File.OpenRead(zipPath))
        If password <> Nothing Then z.Password = password
        If folderPath = Nothing Then
            'folderPath = HttpContext.Current.Server.MapPath("~/")
            folderPath = Application.StartupPath
        End If
        If Not folderPath.EndsWith("\") Then folderPath &= "\"
        If Not Directory.Exists(folderPath) Then Directory.CreateDirectory(folderPath)

        Dim t As ZipEntry
        Dim fp As String

        ' 开始循环解压
        Do
            t = z.GetNextEntry
            If t Is Nothing Then Exit Do
            fp = folderPath & t.Name
            ' 如果是文件夹则创建文件夹
            If t.IsDirectory AndAlso Not Directory.Exists(fp) Then
                Directory.CreateDirectory(fp)
            ElseIf t.IsFile Then
                ' 如果是文件则解压文件
                If (Not File.Exists(fp)) OrElse (File.Exists(fp) And overwrite) Then
                    Dim sw As FileStream = File.Create(fp)
                    Dim size As Integer
                    Dim data(2048) As Byte
                    Do '循环读取数据
                        size = z.Read(data, 0, data.Length)
                        If size = 0 Then Exit Do
                        sw.Write(data, 0, size)
                    Loop
                    sw.Close()
                    File.SetLastWriteTime(fp, t.DateTime) '更新创建日期
                End If

            End If
        Loop

        z.Close()
    End Sub

    '''<summary>压缩某个目录内全部文档到ZIP</summary>
    '''<param name="zipPath">ZIP路径</param>
    '''<param name="folderPath">目标目录</param>
    '''<param name="level">压缩级别</param>
    '''<param name="password">密码</param>
    Public Shared Sub CompressFile(ByVal zipPath As String, ByVal folderPath As String, Optional ByVal level As Integer = 9, Optional ByVal password As String = Nothing)

        If Not Directory.Exists(folderPath) Then Return
        If folderPath.EndsWith("\") Then folderPath &= "\"

        Dim z As New ZipOutputStream(File.Create(zipPath))
        z.SetLevel(level)
        If password <> Nothing Then z.Password = password

        Dim fn As String, buffer(4096) As Byte
        Dim di As Integer = folderPath.Length + 1

        ' 先将主目录下所有文件加入文档
        For Each f As String In Directory.GetFiles(folderPath, "*", SearchOption.TopDirectoryOnly)
            fn = f.Remove(0, di)
            z.PutNextEntry(New ZipEntry(fn))
            ' 将文件数据写入ZIP
            Using fs As FileStream = File.OpenRead(f)
                StreamUtils.Copy(fs, z, buffer)
            End Using
        Next

        ' 枚举所有文件夹
        For Each d As String In Directory.GetDirectories(folderPath, "*", SearchOption.AllDirectories)
            fn = d.Remove(0, di) & "\"
            z.PutNextEntry(New ZipEntry(fn))
            ' 枚举所有文件
            For Each f As String In Directory.GetFiles(d, "*", SearchOption.TopDirectoryOnly)
                fn = f.Remove(0, di)
                z.PutNextEntry(New ZipEntry(fn))
                Using fs As FileStream = File.OpenRead(f)
                    StreamUtils.Copy(fs, z, buffer)
                End Using
            Next
        Next

        z.Close()
    End Sub

End Class