Go标准库之Zip
本包不支持跨硬盘的压缩。
关于ZIP64:
为了向下兼容,FileHeader同时拥有32位和64位的Size字段。64位字段总是包含正确的值,对普通格式的档案未见它们的值是相同的。对zip64格式的档案文件32位字段将是0xffffffff,必须使用64位字段。
// Constants
const (
Store uint16 = 0
Deflate uint16 = 8
)
// Struct
// Header
type File struct {
FileHeader
}
// Function
func (f *File) DataOffset() (offset int64, err error)
// DataOffset返回文件的可能存在的压缩数据相对于 zip 文件起始的偏移量。大多数调用者应使用 Open 代替,该方法会主动解压缩数据并验证校验和。
func (f *File) Open() (rc io.ReadCloser, err error)
// Open方法返回一个 io.ReadCloser 接口,提供读取文件内容的方法。可以同时读取多个文件。
// FileHeader
type FileHeader struct {
Name string
CreatorVersion uint16
ReaderVersion uint16
Flags uint16
Method uint16
ModifiedTime uint16 // MS-DOS时间
ModifiedDate uint16 // MS-DOS日期
CRC32 uint32
CompressedSize uint32 // 已弃用;请使用CompressedSize64
UncompressedSize uint32 // 已弃用;请使用UncompressedSize64
CompressedSize64 uint64
UncompressedSize64 uint64
Extra []byte
ExternalAttrs uint32 // 其含义依赖于CreatorVersion
Comment string
}
// Function
func (h *FileHeader) FileInfo() os.FileInfo
// FileInfo返回一个根据h的信息生成的 os.FileInfo
func (h *FileHeader) ModTime() time.Time
// 返回最近一次修改的UTC时间。(精度2s)
func (h *FileHeader) Mode() (mode os.FileMode)
// Mode返回h的权限和模式位
func (h *FileHeader) SetModTime(t time.Time)
// 将ModifiedTime和ModifiedDate字段设置为给定的UTC时间。(精度2s)
func (h *FileHeader) SetMode(mode os.FileMode)
// SetMode修改h的权限和模式位。
// ReadCloser
type ReadCloser struct {
Reader
}
// Function
func (rc *ReadCloser) Close() error
// Close关闭 zip 文件,使它不能用于I/O。
// Reader
type Reader struct {
r io.ReaderAt
File []*File
Comment string
decompressors map[uint16]Decompressor
}
// Writer
// Writer类型实现了 zip 文件的写入器。
// Writer implements a zip file writer.
type Writer struct {
cw *countWriter
dir []*header
last *fileWriter
closed bool
compressors map[uint16]Compressor
comment string
// testHookCloseSizeOffset if non-nil is called with the size
// of offset of the central directory at Close.
testHookCloseSizeOffset func(size, offset uint64)
}
// Function
func (w *Writer) Close() error
// Close方法通过写入中央目录关闭该* Writer 。本方法不会也没办法关闭下层的 io.Writer 接口。
func (w *Writer) Create(name string) (io.Writer, error)
// 使用给出的文件名添加一个文件进 zip 文件。本方法返回一个 io.Writer 接口(用于写入新添加文件的内容)。文件名必须是相对路径,不能以设备或斜杠开始,只接受’/'作为路径分隔。新增文件的内容必须在下一次调用 CreateHeader 、Create或 Close 方法之前全部写入
func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error)
// 使用给出的*FileHeader来作为文件的元数据添加一个文件进 zip 文件。本方法返回一个 io.Writer 接口(用于写入新添加文件的内容)。新增文件的内容必须在下一次调用CreateHeader、 Create 或 Close 方法之前全部写入。
// Type
type Compressor func(io.Writer) (io.WriteCloser, error)
// Compressor函数类型会返回一个 io.WriteCloser ,该接口会将数据压缩后写入提供的接口。关闭时,应将缓冲中的数据刷新到下层接口中。
type Decompressor func(io.Reader) io.ReadCloser
// Decompressor函数类型会返回一个 io.ReadCloser ,该接口的Read方法会将读取自提供的接口的数据提前解压缩。程序员有责任在读取结束时关闭该 io.ReadCloser 。
// Function
func FileInfoHeader(fi os.FileInfo) (*FileHeader, error)
// FileInfoHeader返回一个根据fi填写了部分字段的Header。因为 os.FileInfo 接口的Name方法只返回它描述的文件的无路径名,有可能需要将返回值的Name字段修改为文件的完整路径名。
func NewReader(r io.ReaderAt, size int64) (*Reader, error)
// NewReader返回一个从r读取数据的*Reader,r被假设其大小为size字节。
func NewWriter(w io.Writer) *Writer
// NewWriter创建并返回一个将 zip 文件写入w的*Writer
func OpenReader(name string) (*ReadCloser, error)
// OpenReader会打开name指定的 zip 文件并返回一个*ReadCloser
func RegisterCompressor(method uint16, comp Compressor)
// RegisterCompressor使用指定的方法ID注册一个Compressor类型函数。常用的方法Store和Deflate是内建的
func RegisterDecompressor(method uint16, d Decompressor)
// RegisterDecompressor使用指定的方法ID注册一个Decompressor类型函数。
Demo
//压缩Demo
func zipFunc() {
zipTarget := "./zipFile.zip"
fileFolder := "./fileFolder"
zipfile, err := os.Create(zipTarget)
if err != nil {
// if file is exist then delete file
if err == os.ErrExist {
if err := os.Remove(zipTarget); err != nil {
fmt.Println(err)
}
} else {
fmt.Println(err)
}
}
defer zipfile.Close()
zipwriter := zip.NewWriter(zipfile)
defer zipwriter.Close()
sfileInfo, err := os.Stat(fileFolder)
if err != nil {
fmt.Println(err)
}
if !sfileInfo.IsDir() {
zipFile(fileFolder, zipwriter)
} else {
zipFolder(fileFolder, zipwriter)
}
}
// 压缩文件
func zipFolder(directory string, zw *zip.Writer) error {
return filepath.Walk(directory, func(zipPath string, file os.FileInfo, err error) error {
//read the file failure
if file == nil {
return err
}
if file.IsDir() {
if directory == zipPath {
return nil
}
fileFolder := filepath.Join(directory, strings.TrimPrefix(zipPath, directory))
os.Mkdir(strings.TrimPrefix(directory, file.Name()), os.ModeDir)
//如果是目录,递归目录
return zipFolder(fileFolder, zw)
}
return zipFile(zipPath, zw)
})
}
// 解压缩函数
func zipFile(sourceFile string, zw *zip.Writer) error {
sfile, err := os.Open(sourceFile)
if err != nil {
fmt.Println(err)
return err
}
defer sfile.Close()
info, err := sfile.Stat()
if err != nil {
fmt.Println(err)
return err
}
// 获取压缩头信息
header, err := zip.FileInfoHeader(info)
if err != nil {
fmt.Println(err)
return err
}
// 指定文件压缩方式 默认为 Store 方式 该方式不压缩文件 只是转换为zip保存
header.Method = zip.Deflate
header.Name = sourceFile
fw, err := zw.CreateHeader(header)
if err != nil {
fmt.Println(err)
return err
}
// 写入文件到压缩包中
_, err = io.Copy(fw, sfile)
if err != nil {
fmt.Println(err)
return err
}
return nil
}
//untarFile 解压
func unZipFile(zipFile string, unzipPath string) error {
//打开要解包的文件,tarFile是要解包的 .tar 文件的路径
reader, err := zip.OpenReader(zipFile)
if err != nil {
fmt.Println(err)
}
//用 tr.Next() 来遍历包中的文件,然后将文件的数据保存到磁盘中
for _, file := range reader.File {
rc, err := file.Open()
//先创建目录
fileName := unzipPath + "/" + file.Name
dir := path.Dir(fileName)
_, err = os.Stat(dir)
//如果err 为空说明文件夹已经存在,就不用创建
if err != nil {
err = os.MkdirAll(dir, os.ModePerm)
if err != nil {
fmt.Println(err)
return err
}
}
//创建空文件,准备写入解压后的数据
fw, er := os.Create(fileName)
if er != nil {
fmt.Println(err)
return er
}
defer fw.Close()
// 写入解压后的数据
_, er = io.CopyN(fw, rc, int64(file.UncompressedSize64))
if er != nil {
return er
}
}
return nil
}
Songzhibin