Imports System Imports System.Collections.Generic Imports System.IO Imports System.IO.Compression
' 此类是一个读取器,它会读取文件基底数据流以便一个接着一个取得压缩文件中项目。 ' 压缩文件项目内含文件名称、大小、压缩后的大小、CRC...等信息。 ' 它同时支持 GZIP 与 DEFLATE 这两种压缩方式。
Public Class ZipReader '更多.net源码和教程,来自[乐博网 www.lob.cn] ' 此封存数据流会在每一次操作之后被初始化与关闭。 Private zipStream As Stream ' 封存的名称。 Private zipName As String ' 被读取之标头与压缩数据的数据流。 Private baseStream As Stream Private numberOfFiles As Int16 Private thisMethod As Byte
Private md5 As System.Security.Cryptography.MD5CryptoServiceProvider
' 用于检查 CRC 。 ' 建立一个新的封存输入数据流,读取一个压缩文件。 Public Sub New(ByVal fileStream As Stream, ByVal name As String) zipName = name baseStream = fileStream numberOfFiles = -1 thisMethod = 255 md5 = New System.Security.Cryptography.MD5CryptoServiceProvider()
End Sub 'New
' 读取超标头。 ' 超标头结构: ' 文件的数目 - 2 字节 ' 压缩的方式 - 1 字节 Private Sub ReadSuperHeader() numberOfFiles = ReadLeInt16() thisMethod = ReadLeByte() If Method <> ZipConstants.DEFLATE AndAlso Method <> ZipConstants.GZIP Then Throw New ArgumentOutOfRangeException() End If End Sub 'ReadSuperHeader
Private Function ReadBuf(ByVal outBuf() As Byte, ByVal length As Integer) As Integer Return baseStream.Read(outBuf, 0, length) End Function 'ReadBuf
' 从 baseStream 读取一个字节。 Private Function ReadLeByte() As Byte Return System.Convert.ToByte(baseStream.ReadByte() And &HFF) End Function 'ReadLeByte
' 以小尾序(Little-Endian)字节顺序来读取一个不带符号的 short 基础数据流。 Private Function ReadLeInt16() As Int16 Dim i As Int16 = CType(ReadLeByte(), Int16) Dim j As Int16 = CType(ReadLeByte(), Int16) Return (i Or (j << 8)) End Function 'ReadLeInt16
' 以小尾序(Little-Endian)字节顺序来读取一个 int 基底数据流。 Private Function ReadLeInt32() As Int32 Dim ui As Int32 Dim uj As Int32 ui = CType(ReadLeInt16(), Int32) uj = CType(ReadLeInt16(), Int32) If (ui < 0) Then ui = System.Math.Pow(2, 16) + ui End If Return (ui Or (uj << 16)) End Function 'ReadLeInt32
Private Function ConvertToString(ByVal data() As Byte) As String Return System.Text.Encoding.ASCII.GetString(data, 0, data.Length) End Function 'ConvertToString
' 从压缩文件读取下一个项目并返回其描述。 Private Function GetNextEntry() As ZipEntry Dim currentEntry As ZipEntry = Nothing Try Dim size As Int32 = ReadLeInt32() If size = -1 Then Return New ZipEntry(String.Empty) End If Dim csize As Int32 = ReadLeInt32() Dim crc(15) As Byte ReadBuf(crc, crc.Length)
Dim dostime As Int32 = ReadLeInt32() Dim nameLength As Int16 = ReadLeInt16()
Dim buffer(nameLength - 1) As Byte ReadBuf(buffer, nameLength) Dim name As String = ConvertToString(buffer)
currentEntry = New ZipEntry(name) currentEntry.Size = size currentEntry.CompressedSize = csize currentEntry.SetCrc(crc) currentEntry.DosTime = dostime Catch ex As ArgumentException ZipConstants.ShowError(ZipConstants.ArgumentError) Catch ex As ObjectDisposedException ZipConstants.ShowError(ZipConstants.CloseError) End Try Return currentEntry End Function 'GetNextEntry
' 将未压缩数据写入项目中的文件名称。 ' 它会初始化一个内存数据流来作为暂存处,并且使用 Gzip 数据流 ' 或 Deflate 数据流来解压缩它。 Private Sub WriteUncompressedFile(ByVal entry As ZipEntry, ByVal completePath As String) Dim ms As New MemoryStream() Try Dim b(entry.CompressedSize - 1) As Byte baseStream.Read(b, 0, CType(entry.CompressedSize, Integer)) If CheckCRC(entry.GetCrc(), b) Then ms.Write(b, 0, b.Length) End If ms.Seek(0, SeekOrigin.Begin) If Method = ZipConstants.DEFLATE Then zipStream = New DeflateStream(ms, CompressionMode.Decompress, False) ElseIf Method = ZipConstants.GZIP Then zipStream = New GZipStream(ms, CompressionMode.Decompress, False) End If
Dim index As Integer = entry.Name.LastIndexOf(ZipConstants.BackSlash) Dim name As String = completePath + entry.Name.Substring(index + 1) Dim rewrite As New FileStream(name, FileMode.Create) b = New Byte(entry.Size - 1) {} zipStream.Read(b, 0, CType(entry.Size, Integer))
rewrite.Write(b, 0, CType(entry.Size, Integer)) rewrite.Close() Catch ex As IOException ZipConstants.ShowError(ZipConstants.IOError) Catch ex As ArgumentException ZipConstants.ShowError(ZipConstants.ArgumentError) Finally zipStream.Close() ms.Close() End Try End Sub 'WriteUncompressedFile
' 解压缩清单中的所有项目。 ' 清单可能是空的。 Public Sub ExtractAll(ByVal zipEntries As List(Of ZipEntry), _ ByVal startPath As String) Try Dim dir As New DirectoryInfo(startPath + zipName) If Not dir.Exists Then dir.Create() End If Dim jump As Integer = 3 baseStream.Seek(jump, SeekOrigin.Begin)
Dim entry As ZipEntry For Each entry In zipEntries Dim index1 As Integer = entry.Name.IndexOf(ZipConstants.BackSlash) Dim index2 As Integer = entry.Name.LastIndexOf(ZipConstants.BackSlash) Dim relPath As String = entry.Name.Substring(index1 + 1, index2 - index1) If index1 = 0 Then relPath = String.Empty End If If relPath.Length <> 0 Then dir.CreateSubdirectory(relPath) End If jump = ZipConstants.FixedHeaderSize + entry.NameLength baseStream.Seek(jump, SeekOrigin.Current) WriteUncompressedFile(entry, startPath + zipName + ZipConstants.BackSlash + relPath) Next entry CH2_DemoForm002.statusMessage = String.Format(System.Threading.Thread.CurrentThread.CurrentUICulture, ZipConstants.ExtractMessage, startPath + zipName + ZipConstants.BackSlash) Catch ex As IOException ZipConstants.ShowError(ZipConstants.IOError) Catch ex As OutOfMemoryException ZipConstants.ShowError(ZipConstants.MemoryError) End Try End Sub
' 解压缩指定的项目。 Public Sub Extract(ByVal entry As ZipEntry, ByVal jump As Long, ByVal startPath As String) Try Dim dir As New DirectoryInfo(startPath + zipName) If Not dir.Exists Then dir.Create() End If Dim index1 As Integer = entry.Name.IndexOf(ZipConstants.BackSlash) Dim index2 As Integer = entry.Name.LastIndexOf(ZipConstants.BackSlash) Dim relPath As String = entry.Name.Substring(index1 + 1, index2 - index1) If index1 = 0 Then relPath = String.Empty End If If relPath.Length <> 0 Then dir.CreateSubdirectory(relPath) End If baseStream.Seek(jump, SeekOrigin.Begin) jump = ZipConstants.FixedHeaderSize + entry.NameLength baseStream.Seek(jump, SeekOrigin.Current)
WriteUncompressedFile(entry, startPath + zipName + ZipConstants.BackSlash + relPath) CH2_DemoForm002.statusMessage = String.Format(System.Threading.Thread.CurrentThread.CurrentUICulture, ZipConstants.ExtractMessage, startPath + zipName + ZipConstants.BackSlash) Catch ex As IOException ZipConstants.ShowError(ZipConstants.IOError) Catch ex As OutOfMemoryException ZipConstants.ShowError(ZipConstants.MemoryError) End Try End Sub
' 取得文件中的所有项目。 Public Function GetAllEntries() As List(Of ZipEntry) Dim headers As List(Of ZipEntry) headers = Nothing Try If thisMethod = 255 OrElse numberOfFiles = -1 Then baseStream.Seek(0, SeekOrigin.Begin) ReadSuperHeader() End If headers = New List(Of ZipEntry)(numberOfFiles) baseStream.Seek(3, SeekOrigin.Begin) Dim i As Integer
While i < numberOfFiles Dim entry As ZipEntry = GetNextEntry() headers.Add(entry) baseStream.Seek(entry.CompressedSize, SeekOrigin.Current) i += 1 End While Catch ex As IOException ZipConstants.ShowError(ZipConstants.IOError) End Try
Return headers End Function
' 取得封存档的压缩方式。 Public ReadOnly Property Method() As Byte Get Return thisMethod End Get End Property '更多.net源码和教程,来自[乐博网 www.lob.cn]
' 检查字节数组的 CRC。 Private Function CheckCRC(ByVal crc As Byte(), ByVal data As Byte()) As Boolean Dim newCrc As Byte() = md5.ComputeHash(data) Dim i As Integer
While i < crc.Length If crc(i) <> newCrc(i) Then Return False End If i += 1 End While Return True End Function 'CheckCRC End Class 'ZipReader
|