golang 解压带密码的zip包

目录

  • Zip文件详解
    • ZIP 文件格式主要特性
    • 常用算法
    • Zip格式结构图总览
    • Zip文件结构详解
      • 数据区
        • 本地文件头
        • 文件数据
        • 文件描述
      • 中央目录记录区(核心目录记录区 )
      • 中央目录记录尾部区
    • 压缩包解压过程
      • 方式1 通过解析中央目录区来解压
      • 方式2 通过读取本地文件头来解压
      • 两种解压方式对比
  • golang解压zip包
    • 官方archive/zip
    • 第三方包github.com/yeka/zip
    • 自己实现解压加密的zip包

Zip文件详解

ZIP 文件格式是一种常用的压缩和归档格式,用于将多个文件和目录打包到一个单独的文件中,同时对其内容进行压缩以减少文件大小。ZIP 文件格式的设计旨在支持多种压缩算法、加密和数据完整性校验。以下是 ZIP 文件格式的主要特性和常用算法:

ZIP 文件格式主要特性

  1. 文件头

    • 每个文件都有一个本地文件头和一个中央目录文件头。文件头包含文件名、压缩方法、时间戳、CRC-32 校验和、压缩前后的大小等信息。
  2. 数据描述符

    • 可选的后缀结构,包含文件的 CRC-32 校验和、压缩大小和未压缩大小。
  3. 中央目录

    • ZIP 文件的末尾包含一个中央目录记录,列出了 ZIP 文件中的所有文件和目录的文件头信息,用于快速定位和访问。
  4. 结束记录

    • ZIP 文件末尾的“中央目录结束记录”标识了中央目录的结束,包含中央目录的偏移量和大小等信息。

一个简单的 ZIP 文件可以包含多个文件的本地文件头、压缩数据、中央目录和结束记录。解压工具通过读取中央目录找到各个文件的偏移量和大小,然后根据这些信息读取和解压文件数据。

以下是一个 ZIP 文件结构的简化示例:

[本地文件头1] [文件数据1] [本地文件头2] [文件数据2] ... [中央目录] [中央目录结束记录]

ZIP 文件格式因其广泛的支持和高效的压缩性能,广泛应用于文件归档和传输。DEFLATE 算法是其最常用的压缩算法,提供了良好的平衡点。

常用算法

  1. 压缩算法

    • DEFLATE:这是 ZIP 文件中最常用的压缩算法,由 Phil Katz 发明。它结合了 LZ77 算法和 Huffman 编码,提供了良好的压缩比和解压速度。
    • STORE:不进行任何压缩,仅用于存储数据。适用于已经被压缩过的数据,如 JPEG 图像或 MP3 音频文件。
    • 其他压缩方法:ZIP 规范还支持其他压缩方法,如 BZIP2、LZMA 和 PPMd,但这些方法在实际使用中较为少见。
  2. 加密算法

    • 传统 ZIP 加密:早期的 ZIP 文件使用一种相对简单的对称加密方法,但这种方法的安全性较弱。
    • AES 加密:一些现代的 ZIP 工具支持使用高级加密标准 (AES) 进行加密,提供了更强的安全性。
  3. 校验算法

    • CRC-32:用于每个文件的数据完整性校验。每个文件都有一个 CRC-32 校验和,用于检测数据传输或存储过程中的错误。

Zip格式结构图总览

在这里插入图片描述

Zip文件结构详解

zip格式压缩包主要由三大部分组成:数据区、中央目录记录区(也有叫核心目录记录)、中央目录记录尾部区。

数据区

数据区是由一系列本地文件记录组成,本地文件记录主要是记录了压缩前后文件的元数据以及存放压缩后的文件,组成部分也分为三大部分:本地文件头文件数据文件描述

本地文件头

在这里插入图片描述

local file header signature     4 bytes  (0x04034b50)
version needed to extract       2 bytes
general purpose bit flag        2 bytes
compression method              2 bytes
last mod file time              2 bytes
last mod file date              2 bytes
crc-32                          4 bytes
compressed size                 4 bytes
uncompressed size               4 bytes
file name length                2 bytes
extra field length              2 bytesfile name (variable size)
extra field (variable size)

本地文件头主要是记录了压缩文件的元数据:

  1. loca file header signature:0~3,4个字节,用来存放本地文件头标识,一般为固定值0x04034b50,用于解压时候,读取判断文件头的开始;

  2. version needed to extract:4~5,2个字节,记录解压缩文件所需的最低支持的ZIP规范版本,apk压缩版本默认是20, 即Deflate压缩方式。该字段值=解压所需的最低ZIP规范版本*10,比如,最低支持的ZIP规范版本是2.0,那么该字段的值就是20。每个版本定义如下。

    当前最低功能版本定义如下:(压缩包记录的解压版本都是需要版本*10,比如:2.0 * 10 = 201.0 – 默认值
    1.1 – 文件是卷标
    2.0 – 文件是一个文件夹(目录)
    2.0 – 使用 Deflate 压缩来压缩文件
    2.0 – 使用传统的 PKWARE 加密对文件进行加密
    2.1 – 使用 Deflate64™ 压缩文件
    2.5 – 使用 PKWARE DCL Implode 压缩文件
    2.7 – 文件是补丁数据集
    4.5 – 文件使用 ZIP64 格式扩展
    4.6 – 使用 BZIP2 压缩文件压缩
    5.0 – 文件使用 DES 加密
    5.0 – 文件使用 3DES 加密
    5.0 – 使用原始 RC2 加密对文件进行加密
    5.0 – 使用 RC4 加密对文件进行加密
    5.1 – 文件使用 AES 加密进行加密
    5.1 – 使用更正的 RC2 加密对文件进行加密
    5.2 – 使用更正的 RC2-64 加密对文件进行加密
    6.1 – 使用非 OAEP 密钥包装对文件进行加密
    6.2 – 中央目录加密
    
  3. genaral purpose bit flag:6~7,2个字节,记录通用标志位,第0位为1时(即二进制:00000000 00000001),表示文件被加密,解压时候需要解密;第3位为1时候(即二进制:00000000 00000100),表示有数据描述部分,本地文件头中的 CRC-32、压缩大小和未压缩大小字段都被设置为0(虽然zip规范是这么定义,但是发现有些压缩包即使声明有数据描述部分,但是本地文件头的CRC-32、压缩大小和未压缩大小依然还是设置为真实值) , 正确的值被放在紧跟在压缩数据之后的数据描述部分,apk的通用标志位默认传0即可,也有传2048、2056,目前第15位是PKWARE保留位。

  4. compression method:8~9,2个字节,记录压缩包所用到的压缩方式,apk默认Deflate压缩,传8即可, 要是传0 ,则是不压缩,各种压缩方式对应数值如下:

    0 – The file is stored (no compression)
    1 – The file is Shrunk
    2 – The file is Reduced with compression factor 1
    3 – The file is Reduced with compression factor 2
    4 – The file is Reduced with compression factor 3
    5 – The file is Reduced with compression factor 4
    6 – The file is Imploded
    7 – Reserved for Tokenizing compression algorithm
    8 – The file is Deflated
    9 – Enhanced Deflating using Deflate64™
    10 – PKWARE Data Compression Library Imploding
    11 – Reserved by PKWARE
    12 – File is compressed using BZIP2 algorithm
    
  5. last mod file time:10~11,2个字节,记录文件最后修改时间,是MS-DOS格式编码的时间
    在这里插入图片描述

  6. last mod date time:12~13,2个字节,记录文件最后修改日期,是MS-DOS格式编码的日期

  7. crc-32:14~17,4个字节,记录文件未压缩时的CRC-32校验码

  8. compressed size:18~21,4个字节,记录文件压缩后的大小

  9. uncompressed size:22~25,4个字节,记录文件未压缩的大小

  10. file name length:26~27,2个字节,记录文件名的长度(假设文件名长度为n)

  11. extra field length:28~29,2个字节,记录扩展区的长度(假设扩展区长度为m)

  12. file name:30~30+n,n个字节,记录文件名

  13. extral field:30+n~30+n+m,m个字节,记录扩展数据

文件数据

文件数据紧跟在本地文件头之后,一般是压缩后的文件数据或压缩方式选择不压缩时候,用来存储未压缩文件数据。

文件描述

文件描述符仅在通用位标志的第 3 位被设置为1时才存在。 它是字节对齐的,紧跟在文件数据的最后一个字节之后。当且仅当无法在 .ZIP 文件中查找时才使用此描述符,例如:当输出 的.ZIP 文件是标准输出或不可查找设备时使用文件描述,换句话说,正常情况下都不需要使用。

在这里插入图片描述
( 数据描述符标识不一定有,因为一开始规范是没有的,后面才加上去的)

中央目录记录区(核心目录记录区 )

中心目录区的结构如下。

[file header 1]
.
.
. 
[file header n]
[digital signature]
central file header signature   4 bytes  (0x02014b50)
version made by                 2 bytes
version needed to extract       2 bytes
general purpose bit flag        2 bytes
compression method              2 bytes
last mod file time              2 bytes
last mod file date              2 bytes
crc-32                          4 bytes
compressed size                 4 bytes
uncompressed size               4 bytes
file name length                2 bytes
extra field length              2 bytes
file comment length             2 bytes
disk number start               2 bytes
internal file attributes        2 bytes
external file attributes        4 bytes
relative offset of local header 4 bytesfile name (variable size)
extra field (variable size)
file comment (variable size)

中央目录记录区是有一系列中央目录记录所组成,一条中央目录记录对应数据区中的一个压缩文件记录,中央目录记录由以下部分构成:(中央目录区通常由多个文件头(file header)组成,每一个被压缩的文件都有一个对应的file header(注意,这里不是local file header),用于标识和定位该文件在ZIP文件中的位置。这个文件头和本地文件头类似,记录了被压缩文件的元数据信息,包括文件原始大小,压缩之后的大小,文件注释等。)

在这里插入图片描述

  1. central file header signature:0~3,4个字节,记录核心目录文件头标识,固定值:0x02014b50,用于解压时候,查找判断是否是中央目录的开始位置
  2. version made by:4~5,2个字节,记录压缩所用的版本,同数据区本地文件头的解压所需版本,apk设置20
  3. 6~7:2个字节,记录解压所需的最小版本,同数据区本地文件头的解压所需版本,apk设置20
  4. 8~9:2个字节,通用位标记,同数据区本地文件头的通用位标记
  5. 压缩方法、文件最后修改时间、文件最后修改日期、CRC-32校验码、压缩后大小、未压缩大小、文件名长度、扩展区长度,这几个字段的含义都等同于数据区本地文件头对应字段的含义
  6. file comment length:32~33,2个字节,记录文件注释的长度
  7. disk number start:34~35,2个字节,记录文件开始位置的磁盘编号,一般传0即可
  8. 36~41:内部文件属性、外部文件属性,一般也是传0即可
  9. 42~45:4个字节,记录数据区本地文件头相对于压缩包开始位置的偏移量

数据签名(digital signature):

header signature                4 bytes  (0x05054b50)
size of data                    2 bytes
signature data (variable size)
  • header signature:数字签名起始标识,固定值为0x05054b50。
  • size of data:数字签名数据大小。
  • signature data :签名数据

中央目录记录尾部区

中央目录记录尾部主要作用是用来定位中央目录记录区的开始位置,同时记录压缩包的注释内容:
在这里插入图片描述

end of central dir signature    4 bytes  (0x06054b50)
number of this disk             2 bytes
number of the disk with the
start of the central directory  2 bytes
total number of entries in the
central directory on this disk  2 bytes
total number of entries in
the central directory           2 bytes
size of the central directory   4 bytes
offset of start of central
directory with respect to
the starting disk number        4 bytes
.ZIP file comment length        2 bytes
.ZIP file comment       (variable size)
  1. end of central dir signature:0~3,4个字节,中央目录记录尾部开头标记,固定值:0x06054b50,用于解压时,查找判断中央目录尾部的起始位置
  2. number of this disk:4~5,2个字节,记录中央目录记录尾部区所在磁盘编号
  3. number of the disk with the start of the central directory:6~7,2个字节,记录中央目录开始位置所在的磁盘编号
  4. total number of entries in the central directory on this disk:8~9,2个字节,该磁盘上所记录的核心目录数量
  5. total number of entries in the central directory:10~11,2个字节,zip压缩包中的文件总数
  6. size of the central directory:12~15,4个字节,整个中央目录的大小(以字节为单位)
  7. offset of start of central directory with respect to the starting disk number:16~19,4个字节,中央目录开始位置相对位移
  8. ZIP file comment length:20~21,2个字节,注释内容的长度(假设长度为n)
  9. ZIP file comment:22~22+n,n个字节,注释内容

中央目录结束标识是ZIP文件解压的入口。通过读取中央目录结束标识,解压缩软件可以快速地找到中央目录,并据此解析整个ZIP文件的结构和内容。通过里面的中央核心目录区的大小可以找到对应的中央目录模块,然后根据中央目录文件头中的本地文件头偏移(relative offset of local header)可以寻址到对应的文件,并进行解压。

每个压缩文件都必须且仅有一个中央目录结束标识。如果ZIP文件损坏或结构不正确,可能会导致中央目录结束标识丢失或损坏,从而使得解压缩软件无法正确读取和解析ZIP文件。

压缩包解压过程

方式1 通过解析中央目录区来解压

通过ZIP文件的结构我们发现,ZIP文件的中央目录区保存了所有的文件信息。所以,可以通过中央目录区拿到所有的文件信息并进行解压,步骤如下所示。

在这里插入图片描述

  1. 首先在 ZIP 文件末尾通过中央目录结束标识 (0x06054b50)找到中央目录结束标识数据块。
  2. 通过中央目录结束标识中的中央目录区开始位置偏移找到中央目录区数据块。
  3. 根据中央目录区的File Header中的 local file header的偏移量找到对应的local file header。
  4. 根据 local file header找到对应的file data
  5. 解密 file data(如果需要);
  6. 解压 file data;

方式2 通过读取本地文件头来解压

根据 ZIP 文件格式标准可知,除了 中央目录区, 本地文件头中也包含了每个文件的相关信息。因此,可以基于本地文件头去解压文件数据,其解压流程就可以变为:

  1. 从头开始,通过本地文件头标识搜索对应的 local file header
  2. 读取 local file header并找到file data;
  3. 解密 file data(如果需要);
  4. 解压 file data;

两种解压方式对比

通过两种解压方式可以明显看出,两种解压方式适用的场景不同。

方式1适用场景:

  • 适用于在解压文件已经存在于磁盘上,并且需要解压压缩包中所有的文件。

方式2适用场景:

  • 当文件不在磁盘上,比如从网络接收的数据,想边接收边解压;

  • 需要顺序解压ZIP文件前面的一小部分文件,可以使用这种方式,因为方式1读中央目录区会带来额外的耗时;

  • ZIP文件中的中央目录区遭到损坏;

golang解压zip包

官方archive/zip

golang zip包的解压有官方的zip包(archive/zip),但是官方给的zip解压包代码只有解压不带密码的zip包。

下面给出解压操作的封装:

func Unzip(src, dst string) error {// zip.NewReader() 适合从stream中读取字节序列zf, err := zip.OpenReader(src)if err != nil {return err}defer zf.Close()for _, file := range zf.File {// fmt.Println(file.Name)path := filepath.Join(dst, file.Name)// 如果是目录则创建目录if file.FileInfo().IsDir() {if err = os.MkdirAll(path, 0o644); err != nil {return err}continue}f, err := os.Create(path)if err != nil {return err}reader, err := file.Open()if err != nil {return err}_, err = io.Copy(f, reader)if err != nil {return err}_ = f.Close()_ = reader.Close()}return nil
}func main() {log.Println(Unzip("Go.zip", "./static"))
}

在这里插入图片描述
通常情况下,目录权限设为 0755,文件权限设为 0644 更为合适。

在 Unix 和 Unix-like 操作系统(如 Linux)中,文件权限使用三位八进制数来表示,每一位分别表示所有者(Owner)、组(Group)和其他人(Others)的权限。每一位的权限可以是 1(执行权限),2(写入权限),4(读取权限)的组合。它们的组合值表示特定的权限设置。

权限位的含义:

  • 1:执行权限 (Execute)
  • 2:写入权限 (Write)
  • 4:读取权限 (Read)

这些权限可以相加来组合权限。例如:

  • 7 (4+2+1):读取、写入和执行权限 (Read + Write + Execute)
  • 6 (4+2):读取和写入权限 (Read + Write)
  • 5 (4+1):读取和执行权限 (Read + Execute)
  • 4:读取权限 (Read)
  • 3 (2+1):写入和执行权限 (Write + Execute)
  • 2:写入权限 (Write)
  • 1:执行权限 (Execute)
  • 0:无权限 (No permissions)

权限设置示例:

权限设置通常以三位八进制数表示,例如 0755,每一位代表不同用户类别的权限:

  • 第一位(Owner 权限):7,表示所有者有读取、写入和执行权限。
  • 第二位(Group 权限):5,表示组用户有读取和执行权限。
  • 第三位(Others 权限):5,表示其他用户有读取和执行权限。
rwxr-xr-x
  • r 代表读取权限
  • w 代表写入权限
  • x 代表执行权限
  • - 代表没有该权限

示例解释:

  • 0755:

    • 所有者(Owner)权限:7 (rwx) 读取、写入和执行
    • 组(Group)权限:5 (r-x) 读取和执行
    • 其他人(Others)权限:5 (r-x) 读取和执行
  • 0644:

    • 所有者(Owner)权限:6 (rw-) 读取和写入
    • 组(Group)权限:4 (r–) 读取
    • 其他人(Others)权限:4 (r–) 读取

第三方包github.com/yeka/zip

使用go get github.com/yeka/zip安装后,代码都不需要改变,只是导入的zip包替换为第三方即可:
在这里插入图片描述
在这里插入图片描述
https://github.com/yeka/zip,关于这个库里面的整个代码是在官方的zip库的基础上做了一些修改,并且这个库里面的代码抛弃了注册的功能,直接把解密代码写在了open文件里,废弃了一个很好用的功能。

在这里插入图片描述
在这里插入图片描述

自己实现解压加密的zip包

golang官方的zip代码库,https://golang.google.cn/pkg/archive/zip/#pkg-examples有两个注册接口,一个是压缩和一个是解压的:

在这里插入图片描述
在这里插入图片描述
官方注册压缩器方法示例:

package mainimport ("archive/zip""bytes""compress/flate""io"
)func main() {// Override the default Deflate compressor with a higher compression level.// Create a buffer to write our archive to.buf := new(bytes.Buffer)// Create a new zip archive.w := zip.NewWriter(buf)// Register a custom Deflate compressor.w.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {return flate.NewWriter(out, flate.BestCompression)})// Proceed to add files to w.
}

类比这个案例可以写出:

func main() {zf, _ := zip.OpenReader("")zf.RegisterDecompressor(zip.Deflate, func(r io.Reader) io.ReadCloser {// TODO:解密算法实现return flate.NewReader(r)})
}

接下来实现解密算法(官方链接:https://support.pkware.com/pkzip/appnote):

在这里插入图片描述
zip标准文件:https://pkwaredownloads.blob.core.windows.net/pem/APPNOTE.txt,温馨提示:不要机器翻译成中文。

在这里插入图片描述

ZIP 文件加密算法通常使用一种简单的流加密方法,称为 ZipCrypto。解密过程包括初始化三个 32 位整数 key0, key1, 和 key2,并根据密码和加密的字节数据更新这些值。加密和解密使用同样的逻辑,只是加密是将明文转换为密文,而解密是将密文转换为明文。

type ZipCrypto struct {password []byteKeys     [3]uint32
}func NewZipCrypto(passphrase []byte) *ZipCrypto {z := &ZipCrypto{}z.password = passphrasez.init()return z
}func (z *ZipCrypto) init() {z.Keys[0] = 0x12345678z.Keys[1] = 0x23456789z.Keys[2] = 0x34567890for i := 0; i < len(z.password); i++ {z.updateKeys(z.password[i])}
}func (z *ZipCrypto) updateKeys(byteValue byte) {z.Keys[0] = crc32update(z.Keys[0], byteValue)z.Keys[1] += z.Keys[0] & 0xffz.Keys[1] = z.Keys[1]*134775813 + 1z.Keys[2] = crc32update(z.Keys[2], (byte)(z.Keys[1]>>24))
}func (z *ZipCrypto) magicByte() byte {var t uint32 = z.Keys[2] | 2return byte((t * (t ^ 1)) >> 8)
}func (z *ZipCrypto) Encrypt(data []byte) []byte {length := len(data)chiper := make([]byte, length)for i := 0; i < length; i++ {v := data[i]chiper[i] = v ^ z.magicByte()z.updateKeys(v)}return chiper
}func (z *ZipCrypto) Decrypt(chiper []byte) []byte {length := len(chiper)plain := make([]byte, length)for i, c := range chiper {v := c ^ z.magicByte()z.updateKeys(v)plain[i] = v}return plain
}func crc32update(pCrc32 uint32, bval byte) uint32 {return crc32.IEEETable[(pCrc32^uint32(bval))&0xff] ^ (pCrc32 >> 8)
}

实现加密解密算法后写一个小例子来测试下:

func main() {password := "generalzy"zc := NewZipCrypto(password)// 示例数据data := []byte("Hello, ZipCrypto!")fmt.Printf("Original: %s\n", data)// 加密数据encrypted := zc.Encrypt(data)fmt.Printf("Encrypted: %x\n", encrypted)// 初始化解密器zcDecrypt := NewZipCrypto(password)// 解密数据decrypted := zcDecrypt.Decrypt(encrypted)fmt.Printf("Decrypted: %s\n", decrypted)
}

在这里插入图片描述
要利用 zip 包提供的注册方法来注册解密函数,可以使用 RegisterDecompressor 方法。首先,需要扩展之前的 ZipCrypto 实现,使其能够解密压缩数据流。然后,可以将这个解密流注册到 zip 包中。

最后给出整体代码:

package mainimport ("archive/zip""bytes""compress/flate""fmt""hash/crc32""io""io/ioutil""os""path/filepath"
)type ZipCrypto struct {password []byteKeys     [3]uint32
}func NewZipCrypto(passphrase []byte) *ZipCrypto {z := &ZipCrypto{}z.password = passphrasez.init()return z
}func (z *ZipCrypto) init() {z.Keys[0] = 0x12345678z.Keys[1] = 0x23456789z.Keys[2] = 0x34567890for i := 0; i < len(z.password); i++ {z.updateKeys(z.password[i])}
}func (z *ZipCrypto) updateKeys(byteValue byte) {z.Keys[0] = crc32update(z.Keys[0], byteValue)z.Keys[1] += z.Keys[0] & 0xffz.Keys[1] = z.Keys[1]*134775813 + 1z.Keys[2] = crc32update(z.Keys[2], (byte)(z.Keys[1]>>24))
}func (z *ZipCrypto) magicByte() byte {var t uint32 = z.Keys[2] | 2return byte((t * (t ^ 1)) >> 8)
}func (z *ZipCrypto) Encrypt(data []byte) []byte {length := len(data)chiper := make([]byte, length)for i := 0; i < length; i++ {v := data[i]chiper[i] = v ^ z.magicByte()z.updateKeys(v)}return chiper
}func (z *ZipCrypto) Decrypt(chiper []byte) []byte {length := len(chiper)plain := make([]byte, length)for i, c := range chiper {v := c ^ z.magicByte()z.updateKeys(v)plain[i] = v}return plain
}func crc32update(pCrc32 uint32, bval byte) uint32 {return crc32.IEEETable[(pCrc32^uint32(bval))&0xff] ^ (pCrc32 >> 8)
}func ZipCryptoDecryptor(r *io.SectionReader, password []byte) (*io.SectionReader, error) {z := NewZipCrypto(password)b := make([]byte, r.Size())r.Read(b)m := z.Decrypt(b)return io.NewSectionReader(bytes.NewReader(m), 12, int64(len(m))), nil
}type unzip struct {offset int64fp     *os.Filename   string
}func (uz *unzip) init() (err error) {uz.fp, err = os.Open(uz.name)return err
}func (uz *unzip) close() {if uz.fp != nil {uz.fp.Close()}
}func (uz *unzip) Size() int64 {if uz.fp == nil {if err := uz.init(); err != nil {return -1}}fi, err := uz.fp.Stat()if err != nil {return -1}return fi.Size() - uz.offset
}func (uz *unzip) ReadAt(p []byte, off int64) (int, error) {if uz.fp == nil {if err := uz.init(); err != nil {return 0, err}}return uz.fp.ReadAt(p, off+uz.offset)
}// DeCompressZip 解压zip包
func DeCompressZip(zipFile, dest, passwd string, offset int64) error {uz := &unzip{offset: offset, name: zipFile}defer uz.close()zr, err := zip.NewReader(uz, uz.Size())if err != nil {return err}if passwd != "" {// Register a custom Deflate compressor.zr.RegisterDecompressor(zip.Deflate, func(r io.Reader) io.ReadCloser {rs := r.(*io.SectionReader)r, _ = ZipCryptoDecryptor(rs, []byte(passwd))return flate.NewReader(r)})zr.RegisterDecompressor(zip.Store, func(r io.Reader) io.ReadCloser {rs := r.(*io.SectionReader)r, _ = ZipCryptoDecryptor(rs, []byte(passwd))return ioutil.NopCloser(r)})}for _, f := range zr.File {fpath := filepath.Join(dest, f.Name)if f.FileInfo().IsDir() {os.MkdirAll(fpath, os.ModePerm)continue}if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {return err}inFile, err := f.Open()if err != nil {return err}outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())if err != nil {inFile.Close()return err}_, err = io.Copy(outFile, inFile)inFile.Close()outFile.Close()if err != nil {return err}}return nil
}func main() {err := DeCompressZip("Go.zip", "./tmp", "123456", 0)if err != nil {fmt.Println(err)}return
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/47367.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[言简意赅] Matlab生成FPGA端rom初始化文件.coe

&#x1f38e;Matlab生成FPGA端rom初始化文件.coe 本文主打言简意赅。 函数源码 function gencoeInitialROM(width, depth, signal, filepath)% gencoeInitialROM - 生成 Xilinx ROM 初始化格式的 COE 文件%% 输入参数:% width - ROM 数据位宽% depth - ROM 数据深度% s…

heic文件怎么转换成jpg?上百份文件转换3秒就能搞定(办公必备)

heic和jpg是两种不同的图片格式&#xff0c;平时整理图片素材时&#xff0c;如果需要将heic转为jpg格式&#xff0c;那么可以使用相关的heic图片转换工具。 ​ 为什么要将heic文件转换成jpg&#xff1f;虽然HEIC格式具有很多优点&#xff0c;但是目前并不是所有设备和应用程序…

好玩模拟游戏推荐:缺氧:眼冒金星 单机游戏分享

《缺氧》 是一款太空殖民模拟游戏。 在外太空岩深处&#xff0c;你手下的勤劳开拓者们需要熟练掌握科技&#xff0c;战胜新的陌生生命形式&#xff0c;以及利用难以置信的太空技术来生存。甚至&#xff0c;还有可能繁荣起来。 建立广阔的基地以及探索生存所需的资源&#xff1…

服务攻防_01数据库安全RedisCouchdbH2database

一、数据库-Redis-未授权RCE&CVE 1、未授权访问&#xff1a;CNVD-2015-07557 &#xff08;1&#xff09;漏洞描述 Redis默认情况下会绑定在6379端口 如果没有采取相关策略&#xff08;如添加防火墙规则阻止非信任来源IP访问&#xff09;&#xff0c;会将Redis暴露在公网…

HTML5实现好看的天气预报网站源码

文章目录 1.设计来源1.1 获取天气接口1.2 PC端页面设计1.3 手机端页面设计 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_4…

揭秘电子画册制作流程,打造独一无二的作品

在这个数字化的时代&#xff0c;电子画册已经成为了展示个人创意和品牌形象的重要工具。它不仅能够呈现出丰富多彩的内容&#xff0c;还能够实现互动性和传播性&#xff0c;吸引众多观众的目光。然而&#xff0c;许多人对于电子画册的制作流程仍然感到陌生。本文将揭秘电子画册…

企业VR展厅如何提升品牌形象,生动展示产品和企业文化?

一、提升产品展示效果 1、全方位展示产品细节 企业VR展厅可以通过3D建模和虚拟现实技术&#xff0c;将产品的每一个细节清晰地展示出来。客户可以全方位查看产品的外观、结构和功能。这种身临其境的体验远比传统的平面展示更加生动和详细。 细节展示&#xff1a;客户可以通过…

Ubuntu22 Qt6.6 ROS 环境搭建

Ubuntu22.04; Qt6.6; Qt Creator 13.01; ROS2 1. 安装 Qt ROS 插件 1.下载地址&#xff1a; https://github.com/ros-industrial/ros_qtc_plugin/releases 选择对应 Qt Creator 版本的安装包。 2. Qt Creator中&#xff0c;“Help - 关于插件”–>“install Plugin…

一个模板实现的工厂的编译问题的解决。牵扯到重载、特化等

简介 在一个项目里&#xff0c;调用了第三封的库&#xff0c;这个库里面有个类用的很多&#xff0c;而且其构造函数至少有6个&#xff0c;并且个人感觉还不够多。根据实际使用&#xff0c;还得增加一些。 需求 1、增加构造函数&#xff0c;比如除了下面的&#xff0c;还增加…

Gateway源码分析:路由Route、断言Predicate、Filter

文章目录 源码总流程图说明GateWayAutoConfigurationDispatcherHandlergetHandler()handleRequestWith()RouteToRequestUrlFilterReactiveLoadBalancerClientFilterNettyRoutingFilter 补充知识适配器模式 详细流程图 源码总流程图 在线总流程图 说明 Gateway的版本使用的是…

01常见控件

文章目录 控件各种响应事件获取控件类型CButton/CheckBox&#xff08;多选&#xff09;/RadioButton&#xff08;单选&#xff09;EditControl&#xff08;文本编辑框&#xff09;/ ListBox&#xff08;列表文本框&#xff09;/ComboBox&#xff08;可下拉列表&#xff09;Prog…

【Ubuntu】Ubuntu系统镜像

清华镜像源 Index of /ubuntu-releases/ | 清华大学开源软件镜像站 | Tsinghua Open Source MirrorIndex of /ubuntu-releases/ | 清华大学开源软件镜像站&#xff0c;致力于为国内和校内用户提供高质量的开源软件镜像、Linux 镜像源服务&#xff0c;帮助用户更方便地获取开源软…

stm32学习:(寄存器2)GPIO总体说明

目录 GPIO的主要特点 GPIO的8种工作模式 GPIO电路结构 GPIO输出模式 输出流程 复用输出模式 GPIO输入模式 输入流程 模拟输入流程 GPIO相关的7个寄存器 GPIOx_CRL GPIOx_CRH GPIOx_IDR GPIOx_ODR GPIOx_BSRR GPIOx_BRR GPIOx_LCKR 实例 三个灯流水灯 main.…

C语言基础 9. 指针

C语言基础 9. 指针 文章目录 C语言基础 9. 指针9.1. &9.2. 指针9.3. 指针的使用9.4. 指针与数组9.5. 指针与const9.6. 指针运算9.7. 动态内存分配 9.1. & 运算符&: scanf(“%d”, &i);里的& 获得变量的地址, 它的操作数必须是变量 int i;printf(“%x”, &…

【SpringBoot Web开发之静态资源访问】笔记

详细内容见官方文档&#xff1a;Static Content SpringBoot Web开发之静态资源访问 1.准备工作&#xff1a;创建WebDemo2.静态资源目录2.1官网原文2.2静态资源目录第一步&#xff1a;依照上面2.1官网原文中创建如下目录第二步&#xff1a;复制粘贴图片到静态资源目录中第三步…

MySQL:JOIN 多表查询

多表查询 在关系型数据库中&#xff0c;表与表之间是有联系的&#xff0c;它们通过 外键 联系在一起&#xff0c;所以在实际应用中&#xff0c;经常使用多表查询。多表查询就是同时查询两个或两个以上的表。 MySQL多表查询是数据库操作中非常重要的一部分&#xff0c;它允许你…

【深入理解SpringCloud微服务】浅析微服务注册中心Eureka与nacos,手写实现一个微服务注册中心

【深入理解SpringCloud微服务】浅析微服务注册中心Eureka与nacos&#xff0c;手写实现一个微服务注册中心 注册中心手写实现一个注册中心服务端设计客户端设计 注册中心 注册中心是微服务体系里面非常重要的一个核心组件&#xff0c;它最重要的作用就是实现服务注册与发现。 …

【MyBatisPlus】快速掌握MP插件使用方法

一、MyBatis-Plus简介 1.1 简介 1.2 特性 无侵入&#xff1a;只做增强不做改变&#xff0c;引入它不会对现有工程产生影响&#xff0c;如丝般顺滑损耗小&#xff1a;启动即会自动注入基本 CURD&#xff0c;性能基本无损耗&#xff0c;直接面向对象操作强大的 CRUD 操作&#x…

【ACM独立出版|EI检索稳定】2024年智能感知与模式识别国际学术会议(ISPC 2024,9月6日-8)

2024年智能感知与模式识别国际学术会议 (ISPC 2024)将于2024年9月6日-8日于中国青岛召开。 会议将围绕智能感知与模式识别等领域中的最新研究成果&#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师等提供一个分享专业经验&#xff0c;扩大…

初谈Linux信号-=-信号的产生

文章目录 概述从生活角度理解信号Linux中信号信号常见的处理方式理解信号的发送与保存 信号的产生core、term区别 概述 从生活角度理解信号 你在网上买了很多件商品&#xff0c;再等待不同商品快递的到来。但即便快递没有到来&#xff0c;你也知道快递来临时&#xff0c; 你该…